diff --git a/os/gcache/gcache.go b/os/gcache/gcache.go index 094aaed6f..31b010686 100644 --- a/os/gcache/gcache.go +++ b/os/gcache/gcache.go @@ -13,20 +13,20 @@ import "time" var cache = New() // Set sets cache with - pair, which is expired after . -// It does not expire if <= 0. +// It does not expire if == 0. func Set(key interface{}, value interface{}, duration time.Duration) { cache.Set(key, value, duration) } // SetIfNotExist sets cache with - pair if does not exist in the cache, -// which is expired after . It does not expire if <= 0. +// which is expired after . It does not expire if == 0. func SetIfNotExist(key interface{}, value interface{}, duration time.Duration) bool { return cache.SetIfNotExist(key, value, duration) } // Sets batch sets cache with key-value pairs by , which is expired after . // -// It does not expire if <= 0. +// It does not expire if == 0. func Sets(data map[interface{}]interface{}, duration time.Duration) { cache.Sets(data, duration) } @@ -41,21 +41,21 @@ func Get(key interface{}) interface{} { // or sets - pair and returns if does not exist in the cache. // The key-value pair expires after . // -// It does not expire if <= 0. +// It does not expire if == 0. func GetOrSet(key interface{}, value interface{}, duration time.Duration) interface{} { return cache.GetOrSet(key, value, duration) } // GetOrSetFunc returns the value of , or sets with result of function // and returns its result if does not exist in the cache. The key-value pair expires -// after . It does not expire if <= 0. +// after . It does not expire if == 0. func GetOrSetFunc(key interface{}, f func() interface{}, duration time.Duration) interface{} { return cache.GetOrSetFunc(key, f, duration) } // GetOrSetFuncLock returns the value of , or sets with result of function // and returns its result if does not exist in the cache. The key-value pair expires -// after . It does not expire if <= 0. +// after . It does not expire if == 0. // // Note that the function is executed within writing mutex lock. func GetOrSetFuncLock(key interface{}, f func() interface{}, duration time.Duration) interface{} { diff --git a/os/gcache/gcache_mem_cache.go b/os/gcache/gcache_mem_cache.go index d38b6b4e0..58d10c7c5 100644 --- a/os/gcache/gcache_mem_cache.go +++ b/os/gcache/gcache_mem_cache.go @@ -21,23 +21,43 @@ import ( // Internal cache object. type memCache struct { - dataMu sync.RWMutex - expireTimeMu sync.RWMutex - expireSetMu sync.RWMutex + // dataMu ensures the concurrent safety of underlying data map. + dataMu sync.RWMutex - // limits the size of the cache pool. - // If the size of the cache exceeds the , + // expireTimeMu ensures the concurrent safety of expireTimes map. + expireTimeMu sync.RWMutex + + // expireSetMu ensures the concurrent safety of expireSets map. + expireSetMu sync.RWMutex + + // cap limits the size of the cache pool. + // If the size of the cache exceeds the cap, // the cache expiration process performs according to the LRU algorithm. // It is 0 in default which means no limits. - cap int - data map[interface{}]memCacheItem // Underlying cache data which is stored in a hash table. - expireTimes map[interface{}]int64 // Expiring key mapping to its timestamp, which is used for quick indexing and deleting. - expireSets map[int64]*gset.Set // Expiring timestamp mapping to its key set, which is used for quick indexing and deleting. + cap int - lru *memCacheLru // LRU manager, which is enabled when > 0. - lruGetList *glist.List // LRU history according with Get function. - eventList *glist.List // Asynchronous event list for internal data synchronization. - closed *gtype.Bool // Is this cache closed or not. + // data is the underlying cache data which is stored in a hash table. + data map[interface{}]memCacheItem + + // expireTimes is the expiring key to its timestamp mapping, + // which is used for quick indexing and deleting. + expireTimes map[interface{}]int64 + + // expireSets is the expiring timestamp to its key set mapping, + // which is used for quick indexing and deleting. + expireSets map[int64]*gset.Set + + // lru is the LRU manager, which is enabled when attribute cap > 0. + lru *memCacheLru + + // lruGetList is the LRU history according with Get function. + lruGetList *glist.List + + // eventList is the asynchronous event list for internal data synchronization. + eventList *glist.List + + // closed controls the cache closed or not. + closed *gtype.Bool } // Internal cache item. @@ -53,7 +73,7 @@ type memCacheEvent struct { } const ( - // Default expire time for no expiring items. + // gDEFAULT_MAX_EXPIRE is the default expire time for no expiring items. // It equals to math.MaxInt64/1000000. gDEFAULT_MAX_EXPIRE = 9223372036854 ) @@ -75,6 +95,59 @@ func newMemCache(lruCap ...int) *memCache { return c } +// Set sets cache with - pair, which is expired after . +// +// It does not expire if == 0. +func (c *memCache) Set(key interface{}, value interface{}, duration time.Duration) { + expireTime := c.getInternalExpire(duration) + c.dataMu.Lock() + c.data[key] = memCacheItem{ + v: value, + e: expireTime, + } + c.dataMu.Unlock() + c.eventList.PushBack(&memCacheEvent{ + k: key, + e: expireTime, + }) +} + +// doSetWithLockCheck sets cache with - pair if does not exist in the +// cache, which is expired after . +// +// It does not expire if == 0. +// The parameter can be type of , but it dose nothing if its +// result is nil. +// +// It doubly checks the whether exists in the cache using mutex writing lock +// before setting it to the cache. +func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) interface{} { + expireTimestamp := c.getInternalExpire(duration) + c.dataMu.Lock() + defer c.dataMu.Unlock() + if v, ok := c.data[key]; ok && !v.IsExpired() { + return v.v + } + if f, ok := value.(func() interface{}); ok { + value = f() + if value == nil { + return nil + } + } + c.data[key] = memCacheItem{v: value, e: expireTimestamp} + c.eventList.PushBack(&memCacheEvent{k: key, e: expireTimestamp}) + return value +} + +// getInternalExpire converts and returns the expire time with given expired duration in milliseconds. +func (c *memCache) getInternalExpire(duration time.Duration) int64 { + if duration == 0 { + return gDEFAULT_MAX_EXPIRE + } else { + return gtime.TimestampMilli() + duration.Nanoseconds()/1000000 + } +} + // makeExpireKey groups the in milliseconds to its according seconds. func (c *memCache) makeExpireKey(expire int64) int64 { return int64(math.Ceil(float64(expire/1000)+1) * 1000) @@ -104,53 +177,8 @@ func (c *memCache) getOrNewExpireSet(expire int64) (expireSet *gset.Set) { return } -// Set sets cache with - pair, which is expired after . -// -// It does not expire if <= 0. -func (c *memCache) Set(key interface{}, value interface{}, duration time.Duration) { - expireTime := c.getInternalExpire(duration.Nanoseconds() / 1000000) - c.dataMu.Lock() - c.data[key] = memCacheItem{v: value, e: expireTime} - c.dataMu.Unlock() - c.eventList.PushBack(&memCacheEvent{k: key, e: expireTime}) -} - -// doSetWithLockCheck sets cache with - pair if does not exist in the cache, -// which is expired after . -// -// It does not expire if <= 0. -// -// It doubly checks the whether exists in the cache using mutex writing lock -// before setting it to the cache. -func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) interface{} { - expireTimestamp := c.getInternalExpire(duration.Nanoseconds() / 1000000) - c.dataMu.Lock() - defer c.dataMu.Unlock() - if v, ok := c.data[key]; ok && !v.IsExpired() { - return v.v - } - if f, ok := value.(func() interface{}); ok { - value = f() - } - if value == nil { - return nil - } - c.data[key] = memCacheItem{v: value, e: expireTimestamp} - c.eventList.PushBack(&memCacheEvent{k: key, e: expireTimestamp}) - return value -} - -// getInternalExpire returns the expire time with given expire duration in milliseconds. -func (c *memCache) getInternalExpire(expire int64) int64 { - if expire <= 0 { - return gDEFAULT_MAX_EXPIRE - } else { - return gtime.TimestampMilli() + expire - } -} - // SetIfNotExist sets cache with - pair if does not exist in the cache, -// which is expired after . It does not expire if <= 0. +// which is expired after . It does not expire if == 0. func (c *memCache) SetIfNotExist(key interface{}, value interface{}, duration time.Duration) bool { if !c.Contains(key) { c.doSetWithLockCheck(key, value, duration) @@ -161,14 +189,20 @@ func (c *memCache) SetIfNotExist(key interface{}, value interface{}, duration ti // Sets batch sets cache with key-value pairs by , which is expired after . // -// It does not expire if <= 0. +// It does not expire if == 0. func (c *memCache) Sets(data map[interface{}]interface{}, duration time.Duration) { - expireTime := c.getInternalExpire(duration.Nanoseconds() / 1000000) + expireTime := c.getInternalExpire(duration) for k, v := range data { c.dataMu.Lock() - c.data[k] = memCacheItem{v: v, e: expireTime} + c.data[k] = memCacheItem{ + v: v, + e: expireTime, + } c.dataMu.Unlock() - c.eventList.PushBack(&memCacheEvent{k: k, e: expireTime}) + c.eventList.PushBack(&memCacheEvent{ + k: k, + e: expireTime, + }) } } @@ -190,7 +224,7 @@ func (c *memCache) Get(key interface{}) interface{} { // GetOrSet returns the value of , or sets - pair and returns if // does not exist in the cache. The key-value pair expires after . It does not expire -// if <= 0. +// if == 0. func (c *memCache) GetOrSet(key interface{}, value interface{}, duration time.Duration) interface{} { if v := c.Get(key); v == nil { return c.doSetWithLockCheck(key, value, duration) @@ -201,7 +235,10 @@ func (c *memCache) GetOrSet(key interface{}, value interface{}, duration time.Du // GetOrSetFunc returns the value of , or sets with result of function // and returns its result if does not exist in the cache. The key-value pair expires -// after . It does not expire if <= 0. +// after . +// +// It does not expire if == 0. +// It does nothing if function returns nil. func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, duration time.Duration) interface{} { if v := c.Get(key); v == nil { return c.doSetWithLockCheck(key, f(), duration) @@ -212,7 +249,10 @@ func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, duration // GetOrSetFuncLock returns the value of , or sets with result of function // and returns its result if does not exist in the cache. The key-value pair expires -// after . It does not expire if <= 0. +// after . +// +// It does not expire if == 0. +// It does nothing if function returns nil. // // Note that the function is executed within writing mutex lock. func (c *memCache) GetOrSetFuncLock(key interface{}, f func() interface{}, duration time.Duration) interface{} { @@ -238,7 +278,10 @@ func (c *memCache) Remove(key interface{}) (value interface{}) { c.dataMu.Lock() delete(c.data, key) c.dataMu.Unlock() - c.eventList.PushBack(&memCacheEvent{k: key, e: gtime.TimestampMilli() - 1000}) + c.eventList.PushBack(&memCacheEvent{ + k: key, + e: gtime.TimestampMilli() - 1000, + }) } return } @@ -310,18 +353,20 @@ func (c *memCache) Close() { c.closed.Set(true) } -// Asynchronous task loop: -// 1. asynchronously process the data in the event list, +// syncEventAndClearExpired does the asynchronous task loop: +// 1. Asynchronously process the data in the event list, // and synchronize the results to the and properties. -// 2. clean up the expired key-value pair data. +// 2. Clean up the expired key-value pair data. func (c *memCache) syncEventAndClearExpired() { - event := (*memCacheEvent)(nil) - oldExpireTime := int64(0) - newExpireTime := int64(0) if c.closed.Val() { gtimer.Exit() return } + var ( + event *memCacheEvent + oldExpireTime int64 + newExpireTime int64 + ) // ======================== // Data Synchronization. // ======================== @@ -365,10 +410,13 @@ func (c *memCache) syncEventAndClearExpired() { // ======================== // Data Cleaning up. // ======================== - ek := c.makeExpireKey(gtime.TimestampMilli()) - eks := []int64{ek - 1000, ek - 2000, ek - 3000, ek - 4000, ek - 5000} + var ( + expireSet *gset.Set + ek = c.makeExpireKey(gtime.TimestampMilli()) + eks = []int64{ek - 1000, ek - 2000, ek - 3000, ek - 4000, ek - 5000} + ) for _, expireTime := range eks { - if expireSet := c.getExpireSet(expireTime); expireSet != nil { + if expireSet = c.getExpireSet(expireTime); expireSet != nil { // Iterating the set to delete all keys in it. expireSet.Iterator(func(key interface{}) bool { c.clearByKey(key)