gitee.com/zhongguo168a/gocodes@v0.0.0-20230609140523-e1828349603f/myx/cachex/expired.go (about)

     1  package cachex
     2  
     3  import (
     4  	"time"
     5  )
     6  
     7  type expiredItem struct {
     8  	key     string
     9  	handler func(key string, data interface{})
    10  	time    time.Time
    11  
    12  	next *expiredItem
    13  	prev *expiredItem
    14  }
    15  
    16  func (item *expiredItem) remove() {
    17  	item_prev := item.prev
    18  	if item_prev != nil {
    19  		item_prev.next = item.next
    20  	}
    21  
    22  	item_next := item.next
    23  	if item_next != nil {
    24  		item_next.prev = item.prev
    25  	}
    26  
    27  	item.next = nil
    28  	item.prev = nil
    29  
    30  }
    31  
    32  func (item *expiredItem) insertAfter(new *expiredItem) {
    33  	item_next := item.next
    34  	item.next, new.prev = new, item
    35  	if item_next != nil {
    36  		item.next.prev = new
    37  		new.next = item_next
    38  	}
    39  }
    40  
    41  func (item *expiredItem) insertBefore(new *expiredItem) {
    42  	item_prev := item.prev
    43  	item.prev, new.next = new, item
    44  	if item_prev != nil {
    45  		item_prev.next = new
    46  		new.prev = item_prev
    47  	}
    48  }
    49  
    50  func (c *Cache) SetExpiredSpace(t time.Duration) {
    51  	c.expiredSpace = t
    52  }
    53  
    54  // 由于采用了标记的方式清楚expire, 所以可能会出现一种情况:
    55  // 清理后, list中残留了一个键为[key]的expiredItem, 但后面只再次Set了[key]的值, 但没有Expire, 会导致expiredItem仍然生效
    56  // 因此, 使用的时候需要注意这点
    57  func (c *Cache) Expire(key string, duration time.Duration, handler func(key string, data interface{})) {
    58  	var (
    59  		start *expiredItem // 起始节点
    60  		right bool         // 是否往右, 即时间递增的方向
    61  	)
    62  	newTime := time.Now().Add(duration)
    63  	newItem, existed := c.expiredSet[key]
    64  	if existed {
    65  		newItem.handler = handler
    66  		if newTime.Equal(newItem.time) {
    67  			return
    68  		}
    69  		if newTime.After(newItem.time) {
    70  			start = newItem.next
    71  			right = true
    72  		} else {
    73  			start = newItem.prev
    74  		}
    75  		newItem.time = newTime
    76  		if start == nil {
    77  			// 不需要处理
    78  			return
    79  		}
    80  
    81  		if newItem == c.expiredRoot {
    82  			c.expiredRoot = newItem.next
    83  		}
    84  		newItem.remove()
    85  
    86  	} else {
    87  		newItem = &expiredItem{
    88  			key:     key,
    89  			handler: handler,
    90  			time:    newTime,
    91  		}
    92  
    93  		c.mu.Lock()
    94  		c.expiredSet[key] = newItem
    95  		c.mu.Unlock()
    96  		// 第一个
    97  		if c.expiredRoot == nil {
    98  			c.expiredRoot = newItem
    99  			return
   100  		}
   101  
   102  		start = c.expiredRoot
   103  		right = true
   104  	}
   105  
   106  	if right {
   107  		cur := start
   108  		for {
   109  			if newItem.time.Before(cur.time) {
   110  				cur.insertBefore(newItem)
   111  				if c.expiredRoot == cur {
   112  					c.expiredRoot = newItem
   113  				}
   114  				break
   115  			}
   116  
   117  			if cur.next == nil {
   118  				cur.insertAfter(newItem)
   119  				break
   120  			}
   121  
   122  			cur = cur.next
   123  		}
   124  	} else {
   125  		cur := start
   126  		for {
   127  			if newItem.time.After(cur.time) {
   128  				cur.insertAfter(newItem)
   129  				break
   130  			}
   131  
   132  			if cur.prev == nil {
   133  				cur.insertBefore(newItem)
   134  				if c.expiredRoot == cur {
   135  					c.expiredRoot = newItem
   136  				}
   137  				break
   138  			}
   139  
   140  			cur = cur.prev
   141  		}
   142  	}
   143  }
   144  
   145  func (c *Cache) cleanExpireKey(key string) {
   146  
   147  }
   148  
   149  func (c *Cache) cleanExpireItem(item *expiredItem) {
   150  	data, has := c.Get(item.key)
   151  	if has && item.handler != nil {
   152  		item.handler(item.key, data)
   153  	}
   154  	item.remove()
   155  	c.mu.Lock()
   156  	delete(c.expiredSet, item.key)
   157  	c.mu.Unlock()
   158  	c.Delete(item.key)
   159  }
   160  
   161  func (c *Cache) goCleanExpireList() {
   162  	go func() {
   163  		for {
   164  			c.cleanExpireList()
   165  			time.Sleep(c.expiredSpace)
   166  		}
   167  	}()
   168  }
   169  
   170  func (c *Cache) cleanExpireList() {
   171  	if c.expiredRoot == nil {
   172  		return
   173  	}
   174  
   175  	now := time.Now()
   176  	cur := c.expiredRoot
   177  	for {
   178  		if now.Before(cur.time) {
   179  			break
   180  		}
   181  		//
   182  		c.expiredRoot = cur.next
   183  		c.cleanExpireItem(cur)
   184  
   185  		if cur.next == nil {
   186  			break
   187  		}
   188  
   189  		cur = cur.next
   190  	}
   191  }