github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/lib/cache/cache.go (about)

     1  // Package cache implements a simple cache where the entries are
     2  // expired after a given time (5 minutes of disuse by default).
     3  package cache
     4  
     5  import (
     6  	"sync"
     7  	"time"
     8  )
     9  
    10  // Cache holds values indexed by string, but expired after a given (5
    11  // minutes by default).
    12  type Cache struct {
    13  	mu             sync.Mutex
    14  	cache          map[string]*cacheEntry
    15  	expireRunning  bool
    16  	expireDuration time.Duration // expire the cache entry when it is older than this
    17  	expireInterval time.Duration // interval to run the cache expire
    18  }
    19  
    20  // New creates a new cache with the default expire duration and interval
    21  func New() *Cache {
    22  	return &Cache{
    23  		cache:          map[string]*cacheEntry{},
    24  		expireRunning:  false,
    25  		expireDuration: 300 * time.Second,
    26  		expireInterval: 60 * time.Second,
    27  	}
    28  }
    29  
    30  // cacheEntry is stored in the cache
    31  type cacheEntry struct {
    32  	value    interface{} // cached item
    33  	err      error       // creation error
    34  	key      string      // key
    35  	lastUsed time.Time   // time used for expiry
    36  }
    37  
    38  // CreateFunc is called to create new values.  If the create function
    39  // returns an error it will be cached if ok is true, otherwise the
    40  // error will just be returned, allowing negative caching if required.
    41  type CreateFunc func(key string) (value interface{}, ok bool, error error)
    42  
    43  // used marks an entry as accessed now and kicks the expire timer off
    44  // should be called with the lock held
    45  func (c *Cache) used(entry *cacheEntry) {
    46  	entry.lastUsed = time.Now()
    47  	if !c.expireRunning {
    48  		time.AfterFunc(c.expireInterval, c.cacheExpire)
    49  		c.expireRunning = true
    50  	}
    51  }
    52  
    53  // Get gets a value named key either from the cache or creates it
    54  // afresh with the create function.
    55  func (c *Cache) Get(key string, create CreateFunc) (value interface{}, err error) {
    56  	c.mu.Lock()
    57  	entry, ok := c.cache[key]
    58  	if !ok {
    59  		c.mu.Unlock() // Unlock in case Get is called recursively
    60  		value, ok, err = create(key)
    61  		if err != nil && !ok {
    62  			return value, err
    63  		}
    64  		entry = &cacheEntry{
    65  			value: value,
    66  			key:   key,
    67  			err:   err,
    68  		}
    69  		c.mu.Lock()
    70  		c.cache[key] = entry
    71  	}
    72  	defer c.mu.Unlock()
    73  	c.used(entry)
    74  	return entry.value, entry.err
    75  }
    76  
    77  // Put puts an value named key into the cache
    78  func (c *Cache) Put(key string, value interface{}) {
    79  	c.mu.Lock()
    80  	defer c.mu.Unlock()
    81  	entry := &cacheEntry{
    82  		value: value,
    83  		key:   key,
    84  	}
    85  	c.used(entry)
    86  	c.cache[key] = entry
    87  }
    88  
    89  // GetMaybe returns the key and true if found, nil and false if not
    90  func (c *Cache) GetMaybe(key string) (value interface{}, found bool) {
    91  	c.mu.Lock()
    92  	defer c.mu.Unlock()
    93  	entry, found := c.cache[key]
    94  	if !found {
    95  		return nil, found
    96  	}
    97  	c.used(entry)
    98  	return entry.value, found
    99  }
   100  
   101  // cacheExpire expires any entries that haven't been used recently
   102  func (c *Cache) cacheExpire() {
   103  	c.mu.Lock()
   104  	defer c.mu.Unlock()
   105  	now := time.Now()
   106  	for key, entry := range c.cache {
   107  		if now.Sub(entry.lastUsed) > c.expireDuration {
   108  			delete(c.cache, key)
   109  		}
   110  	}
   111  	if len(c.cache) != 0 {
   112  		time.AfterFunc(c.expireInterval, c.cacheExpire)
   113  		c.expireRunning = true
   114  	} else {
   115  		c.expireRunning = false
   116  	}
   117  }
   118  
   119  // Clear removes everything from the cahce
   120  func (c *Cache) Clear() {
   121  	c.mu.Lock()
   122  	for k := range c.cache {
   123  		delete(c.cache, k)
   124  	}
   125  	c.mu.Unlock()
   126  }
   127  
   128  // Entries returns the number of entries in the cache
   129  func (c *Cache) Entries() int {
   130  	c.mu.Lock()
   131  	entries := len(c.cache)
   132  	c.mu.Unlock()
   133  	return entries
   134  }