github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/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 pinCount int // non zero if the entry should not be removed 37 } 38 39 // CreateFunc is called to create new values. If the create function 40 // returns an error it will be cached if ok is true, otherwise the 41 // error will just be returned, allowing negative caching if required. 42 type CreateFunc func(key string) (value interface{}, ok bool, error error) 43 44 // used marks an entry as accessed now and kicks the expire timer off 45 // should be called with the lock held 46 func (c *Cache) used(entry *cacheEntry) { 47 entry.lastUsed = time.Now() 48 if !c.expireRunning { 49 time.AfterFunc(c.expireInterval, c.cacheExpire) 50 c.expireRunning = true 51 } 52 } 53 54 // Get gets a value named key either from the cache or creates it 55 // afresh with the create function. 56 func (c *Cache) Get(key string, create CreateFunc) (value interface{}, err error) { 57 c.mu.Lock() 58 entry, ok := c.cache[key] 59 if !ok { 60 c.mu.Unlock() // Unlock in case Get is called recursively 61 value, ok, err = create(key) 62 if err != nil && !ok { 63 return value, err 64 } 65 entry = &cacheEntry{ 66 value: value, 67 key: key, 68 err: err, 69 } 70 c.mu.Lock() 71 c.cache[key] = entry 72 } 73 defer c.mu.Unlock() 74 c.used(entry) 75 return entry.value, entry.err 76 } 77 78 func (c *Cache) addPin(key string, count int) { 79 c.mu.Lock() 80 entry, ok := c.cache[key] 81 if ok { 82 entry.pinCount += count 83 c.used(entry) 84 } 85 c.mu.Unlock() 86 } 87 88 // Pin a value in the cache if it exists 89 func (c *Cache) Pin(key string) { 90 c.addPin(key, 1) 91 } 92 93 // Unpin a value in the cache if it exists 94 func (c *Cache) Unpin(key string) { 95 c.addPin(key, -1) 96 } 97 98 // Put puts a value named key into the cache 99 func (c *Cache) Put(key string, value interface{}) { 100 c.mu.Lock() 101 defer c.mu.Unlock() 102 entry := &cacheEntry{ 103 value: value, 104 key: key, 105 } 106 c.used(entry) 107 c.cache[key] = entry 108 } 109 110 // GetMaybe returns the key and true if found, nil and false if not 111 func (c *Cache) GetMaybe(key string) (value interface{}, found bool) { 112 c.mu.Lock() 113 defer c.mu.Unlock() 114 entry, found := c.cache[key] 115 if !found { 116 return nil, found 117 } 118 c.used(entry) 119 return entry.value, found 120 } 121 122 // Rename renames the item at oldKey to newKey. 123 // 124 // If there was an existing item at newKey then it takes precedence 125 // and is returned otherwise the item (if any) at oldKey is returned. 126 func (c *Cache) Rename(oldKey, newKey string) (value interface{}, found bool) { 127 c.mu.Lock() 128 if newEntry, newFound := c.cache[newKey]; newFound { 129 // If new entry is found use that 130 delete(c.cache, oldKey) 131 value, found = newEntry.value, newFound 132 c.used(newEntry) 133 } else if oldEntry, oldFound := c.cache[oldKey]; oldFound { 134 // If old entry is found rename it to new and use that 135 c.cache[newKey] = oldEntry 136 delete(c.cache, oldKey) 137 c.used(oldEntry) 138 value, found = oldEntry.value, oldFound 139 } 140 c.mu.Unlock() 141 return value, found 142 } 143 144 // cacheExpire expires any entries that haven't been used recently 145 func (c *Cache) cacheExpire() { 146 c.mu.Lock() 147 defer c.mu.Unlock() 148 now := time.Now() 149 for key, entry := range c.cache { 150 if entry.pinCount <= 0 && now.Sub(entry.lastUsed) > c.expireDuration { 151 delete(c.cache, key) 152 } 153 } 154 if len(c.cache) != 0 { 155 time.AfterFunc(c.expireInterval, c.cacheExpire) 156 c.expireRunning = true 157 } else { 158 c.expireRunning = false 159 } 160 } 161 162 // Clear removes everything from the cache 163 func (c *Cache) Clear() { 164 c.mu.Lock() 165 for k := range c.cache { 166 delete(c.cache, k) 167 } 168 c.mu.Unlock() 169 } 170 171 // Entries returns the number of entries in the cache 172 func (c *Cache) Entries() int { 173 c.mu.Lock() 174 entries := len(c.cache) 175 c.mu.Unlock() 176 return entries 177 }