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  }