diff --git a/cache.go b/cache.go index e4e394d..f99f1e5 100644 --- a/cache.go +++ b/cache.go @@ -34,6 +34,8 @@ type Cache struct { type cache struct { DefaultExpiration time.Duration Items map[string]*Item + // TODO: Calls to mu.Unlock are currently not deferred because defer + // adds ~200 ns (as of 792c7561af4b+ tip.) mu sync.Mutex janitor *janitor } @@ -42,9 +44,8 @@ type cache struct { // cache's default expiration time is used. If it is -1, the item never expires. func (c *cache) Set(k string, x interface{}, d time.Duration) { c.mu.Lock() - defer c.mu.Unlock() - c.set(k, x, d) + c.mu.Unlock() } func (c *cache) set(k string, x interface{}, d time.Duration) { @@ -66,13 +67,13 @@ func (c *cache) set(k string, x interface{}, d time.Duration) { // or if the existing item has expired. Returns an error if not. func (c *cache) Add(k string, x interface{}, d time.Duration) error { c.mu.Lock() - defer c.mu.Unlock() - _, found := c.get(k) if found { + c.mu.Unlock() return fmt.Errorf("Item %s already exists", k) } c.set(k, x, d) + c.mu.Unlock() return nil } @@ -80,13 +81,13 @@ func (c *cache) Add(k string, x interface{}, d time.Duration) error { // it does not. func (c *cache) Replace(k string, x interface{}, d time.Duration) error { c.mu.Lock() - defer c.mu.Unlock() - _, found := c.get(k) if !found { + c.mu.Unlock() return fmt.Errorf("Item %s doesn't exist", k) } c.set(k, x, d) + c.mu.Unlock() return nil } @@ -94,9 +95,9 @@ func (c *cache) Replace(k string, x interface{}, d time.Duration) error { // the given key was found in the cache. func (c *cache) Get(k string) (interface{}, bool) { c.mu.Lock() - defer c.mu.Unlock() - - return c.get(k) + x, found := c.get(k) + c.mu.Unlock() + return x, found } func (c *cache) get(k string) (interface{}, bool) { @@ -117,16 +118,16 @@ func (c *cache) get(k string) (interface{}, bool) { // n. Passing a negative number will cause the item to be decremented. func (c *cache) IncrementFloat(k string, n float64) error { c.mu.Lock() - defer c.mu.Unlock() - v, found := c.Items[k] if !found || v.Expired() { + c.mu.Unlock() return fmt.Errorf("Item not found") } t := reflect.TypeOf(v.Object) switch t.Kind() { default: + c.mu.Unlock() return fmt.Errorf("The value of %s is not an integer", k) case reflect.Uint: v.Object = v.Object.(uint) + uint(n) @@ -155,6 +156,7 @@ func (c *cache) IncrementFloat(k string, n float64) error { case reflect.Float64: v.Object = v.Object.(float64) + n } + c.mu.Unlock() return nil } @@ -177,9 +179,8 @@ func (c *cache) Decrement(k string, n int64) error { // Deletes an item from the cache. Does nothing if the item does not exist in the cache. func (c *cache) Delete(k string) { c.mu.Lock() - defer c.mu.Unlock() - c.delete(k) + c.mu.Unlock() } func (c *cache) delete(k string) { @@ -189,13 +190,12 @@ func (c *cache) delete(k string) { // Deletes all expired items from the cache. func (c *cache) DeleteExpired() { c.mu.Lock() - defer c.mu.Unlock() - for k, v := range c.Items { if v.Expired() { c.delete(k) } } + c.mu.Unlock() } // Writes the cache's items (using Gob) to an io.Writer. @@ -262,9 +262,8 @@ func (c *cache) LoadFile(fname string) error { // Deletes all items from the cache. func (c *cache) Flush() { c.mu.Lock() - defer c.mu.Unlock() - c.Items = map[string]*Item{} + c.mu.Unlock() } type janitor struct { diff --git a/cache_test.go b/cache_test.go index 57db9e0..334c224 100644 --- a/cache_test.go +++ b/cache_test.go @@ -675,11 +675,11 @@ func BenchmarkCacheSetDelete(b *testing.B) { func BenchmarkCacheSetDeleteSingleLock(b *testing.B) { tc := New(0, 0) tc.mu.Lock() - defer tc.mu.Unlock() for i := 0; i < b.N; i++ { tc.set("foo", "bar", 0) tc.delete("foo") } + tc.mu.Unlock() } func BenchmarkMapSetDelete(b *testing.B) {