named_cache.go (2061B)
1 // Copyright 2018 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 // Package namedmemcache provides a memory cache with a named lock. This is suitable 15 // for situations where creating the cached resource can be time consuming or otherwise 16 // resource hungry, or in situations where a "once only per key" is a requirement. 17 package namedmemcache 18 19 import ( 20 "sync" 21 22 "github.com/BurntSushi/locker" 23 ) 24 25 // Cache holds the cached values. 26 type Cache struct { 27 nlocker *locker.Locker 28 cache map[string]cacheEntry 29 mu sync.RWMutex 30 } 31 32 type cacheEntry struct { 33 value any 34 err error 35 } 36 37 // New creates a new cache. 38 func New() *Cache { 39 return &Cache{ 40 nlocker: locker.NewLocker(), 41 cache: make(map[string]cacheEntry), 42 } 43 } 44 45 // Clear clears the cache state. 46 func (c *Cache) Clear() { 47 c.mu.Lock() 48 defer c.mu.Unlock() 49 50 c.cache = make(map[string]cacheEntry) 51 c.nlocker = locker.NewLocker() 52 } 53 54 // GetOrCreate tries to get the value with the given cache key, if not found 55 // create will be called and cached. 56 // This method is thread safe. It also guarantees that the create func for a given 57 // key is invoked only once for this cache. 58 func (c *Cache) GetOrCreate(key string, create func() (any, error)) (any, error) { 59 c.mu.RLock() 60 entry, found := c.cache[key] 61 c.mu.RUnlock() 62 63 if found { 64 return entry.value, entry.err 65 } 66 67 c.nlocker.Lock(key) 68 defer c.nlocker.Unlock(key) 69 70 // Create it. 71 value, err := create() 72 73 c.mu.Lock() 74 c.cache[key] = cacheEntry{value: value, err: err} 75 c.mu.Unlock() 76 77 return value, err 78 }