github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/ttlcache/item.go (about) 1 package ttlcache 2 3 import ( 4 "sync" 5 "time" 6 ) 7 8 const ( 9 // NoTTL indicates that an item should never expire. 10 NoTTL time.Duration = -1 11 12 // DefaultTTL indicates that the default TTL 13 // value should be used. 14 DefaultTTL time.Duration = 0 15 ) 16 17 // Item holds all the information that is associated with a single 18 // cache value. 19 type Item[K comparable, V any] struct { 20 // the mutex needs to be locked only when: 21 // - data fields are being read inside accessor methods 22 // - data fields are being updated 23 // when data fields are being read in one of the cache's 24 // methods, we can be sure that these fields are not modified in 25 // parallel since the item list is locked by its own mutex as 26 // well, so locking this mutex would be redundant. 27 // In other words, this mutex is only useful when these fields 28 // are being read from the outside (e.g. in event functions). 29 mu sync.RWMutex 30 key K 31 value V 32 ttl time.Duration 33 expiresAt time.Time 34 queueIndex int 35 } 36 37 // newItem creates a new cache item. 38 func newItem[K comparable, V any](key K, value V, ttl time.Duration) *Item[K, V] { 39 item := &Item[K, V]{ 40 key: key, 41 value: value, 42 ttl: ttl, 43 } 44 item.touch() 45 46 return item 47 } 48 49 // update modifies the item's value and TTL. 50 func (item *Item[K, V]) update(value V, ttl time.Duration) { 51 item.mu.Lock() 52 defer item.mu.Unlock() 53 54 item.value = value 55 item.ttl = ttl 56 57 // reset expiration timestamp because the new TTL may be 58 // 0 or below 59 item.expiresAt = time.Time{} 60 item.touchUnsafe() 61 } 62 63 // touch updates the item's expiration timestamp. 64 func (item *Item[K, V]) touch() { 65 item.mu.Lock() 66 defer item.mu.Unlock() 67 68 item.touchUnsafe() 69 } 70 71 // touchUnsafe updates the item's expiration timestamp without 72 // locking the mutex. 73 func (item *Item[K, V]) touchUnsafe() { 74 if item.ttl <= 0 { 75 return 76 } 77 78 item.expiresAt = time.Now().Add(item.ttl) 79 } 80 81 // IsExpired returns a bool value that indicates whether the item 82 // is expired. 83 func (item *Item[K, V]) IsExpired() bool { 84 item.mu.RLock() 85 defer item.mu.RUnlock() 86 87 return item.isExpiredUnsafe() 88 } 89 90 // isExpiredUnsafe returns a bool value that indicates whether the 91 // the item is expired without locking the mutex 92 func (item *Item[K, V]) isExpiredUnsafe() bool { 93 if item.ttl <= 0 { 94 return false 95 } 96 97 return item.expiresAt.Before(time.Now()) 98 } 99 100 // Key returns the key of the item. 101 func (item *Item[K, V]) Key() K { 102 item.mu.RLock() 103 defer item.mu.RUnlock() 104 105 return item.key 106 } 107 108 // Value returns the value of the item. 109 func (item *Item[K, V]) Value() V { 110 item.mu.RLock() 111 defer item.mu.RUnlock() 112 113 return item.value 114 } 115 116 // TTL returns the TTL value of the item. 117 func (item *Item[K, V]) TTL() time.Duration { 118 item.mu.RLock() 119 defer item.mu.RUnlock() 120 121 return item.ttl 122 } 123 124 // ExpiresAt returns the expiration timestamp of the item. 125 func (item *Item[K, V]) ExpiresAt() time.Time { 126 item.mu.RLock() 127 defer item.mu.RUnlock() 128 129 return item.expiresAt 130 }