From 01842a547caf6a5d39d1db580f08923b840ea7fe Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 14:47:22 -0500 Subject: [PATCH 1/2] Use timevals --- cache.go | 44 +++++++++++++++++++++++++++++++------------- cache_test.go | 5 +++-- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/cache.go b/cache.go index cea90c5..b9bf974 100644 --- a/cache.go +++ b/cache.go @@ -7,22 +7,23 @@ import ( "os" "runtime" "sync" + "syscall" "time" ) -var emptyTime = time.Time{} - type Item struct { Object interface{} - Expiration int64 + Expiration syscall.Timeval } // Returns true if the item has expired. func (item Item) Expired() bool { - if item.Expiration == 0 { + if item.Expiration.Sec == 0 { return false } - return time.Now().UnixNano() > item.Expiration + var tv syscall.Timeval + syscall.Gettimeofday(&tv) + return tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) } const ( @@ -59,12 +60,12 @@ func (c *cache) Set(k string, x interface{}, d time.Duration) { } func (c *cache) set(k string, x interface{}, d time.Duration) { - var e int64 + var e syscall.Timeval if d == DefaultExpiration { d = c.defaultExpiration } if d > 0 { - e = time.Now().Add(d).UnixNano() + e = syscall.NsecToTimeval(time.Now().Add(d).UnixNano()) } c.items[k] = Item{ Object: x, @@ -106,20 +107,36 @@ func (c *cache) Get(k string) (interface{}, bool) { c.mu.RLock() // "Inlining" of get and expired item, found := c.items[k] - if !found || (item.Expiration > 0 && time.Now().UnixNano() > item.Expiration) { + if !found { c.mu.RUnlock() return nil, false } + if item.Expiration.Sec > 0 { + var tv syscall.Timeval + syscall.Gettimeofday(&tv) + if tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) { + c.mu.RUnlock() + return nil, false + } + } c.mu.RUnlock() - return item.Object, found + return item.Object, true } func (c *cache) get(k string) (interface{}, bool) { item, found := c.items[k] - // "Inlining" of expired - if !found || (item.Expiration > 0 && time.Now().UnixNano() > item.Expiration) { + if !found { return nil, false } + // "Inlining" of Expired + if item.Expiration.Sec > 0 { + var tv syscall.Timeval + syscall.Gettimeofday(&tv) + if tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) { + c.mu.RUnlock() + return nil, false + } + } return item.Object, true } @@ -874,11 +891,12 @@ type keyAndValue struct { // Delete all expired items from the cache. func (c *cache) DeleteExpired() { var evictedItems []keyAndValue - now := time.Now().UnixNano() + var now syscall.Timeval + syscall.Gettimeofday(&now) c.mu.Lock() for k, v := range c.items { // "Inlining" of expired - if v.Expiration > 0 && now > v.Expiration { + if v.Expiration.Sec > 0 && (now.Sec > v.Expiration.Sec || (now.Sec == v.Expiration.Sec && now.Usec > v.Expiration.Usec)) { ov, evicted := c.delete(k) if evicted { evictedItems = append(evictedItems, keyAndValue{k, ov}) diff --git a/cache_test.go b/cache_test.go index f604794..62b2854 100644 --- a/cache_test.go +++ b/cache_test.go @@ -6,6 +6,7 @@ import ( "runtime" "strconv" "sync" + "syscall" "testing" "time" ) @@ -110,11 +111,11 @@ func TestNewFrom(t *testing.T) { m := map[string]Item{ "a": Item{ Object: 1, - Expiration: 0, + Expiration: syscall.Timeval{}, }, "b": Item{ Object: 2, - Expiration: 0, + Expiration: syscall.Timeval{}, }, } tc := NewFrom(DefaultExpiration, 0, m) From 2f0c74ebb8a6bd87a32958cdfff1124e527d4718 Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 15:02:02 -0500 Subject: [PATCH 2/2] Use intermediary timevals --- cache.go | 31 +++++++++++++++++-------------- cache_test.go | 5 ++--- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/cache.go b/cache.go index b9bf974..a573611 100644 --- a/cache.go +++ b/cache.go @@ -13,17 +13,17 @@ import ( type Item struct { Object interface{} - Expiration syscall.Timeval + Expiration int64 } // Returns true if the item has expired. func (item Item) Expired() bool { - if item.Expiration.Sec == 0 { + if item.Expiration == 0 { return false } var tv syscall.Timeval syscall.Gettimeofday(&tv) - return tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) + return tv.Nano() > item.Expiration } const ( @@ -60,12 +60,12 @@ func (c *cache) Set(k string, x interface{}, d time.Duration) { } func (c *cache) set(k string, x interface{}, d time.Duration) { - var e syscall.Timeval + var e int64 if d == DefaultExpiration { d = c.defaultExpiration } if d > 0 { - e = syscall.NsecToTimeval(time.Now().Add(d).UnixNano()) + e = time.Now().Add(d).UnixNano() } c.items[k] = Item{ Object: x, @@ -105,16 +105,16 @@ func (c *cache) Replace(k string, x interface{}, d time.Duration) error { // whether the key was found. func (c *cache) Get(k string) (interface{}, bool) { c.mu.RLock() - // "Inlining" of get and expired + // "Inlining" of get and Expired item, found := c.items[k] if !found { c.mu.RUnlock() return nil, false } - if item.Expiration.Sec > 0 { + if item.Expiration > 0 { var tv syscall.Timeval syscall.Gettimeofday(&tv) - if tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) { + if tv.Nano() > item.Expiration { c.mu.RUnlock() return nil, false } @@ -129,10 +129,10 @@ func (c *cache) get(k string) (interface{}, bool) { return nil, false } // "Inlining" of Expired - if item.Expiration.Sec > 0 { + if item.Expiration > 0 { var tv syscall.Timeval syscall.Gettimeofday(&tv) - if tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) { + if tv.Nano() > item.Expiration { c.mu.RUnlock() return nil, false } @@ -890,13 +890,16 @@ type keyAndValue struct { // Delete all expired items from the cache. func (c *cache) DeleteExpired() { - var evictedItems []keyAndValue - var now syscall.Timeval - syscall.Gettimeofday(&now) + var ( + evictedItems []keyAndValue + tv syscall.Timeval + ) + syscall.Gettimeofday(&tv) + now := tv.Nano() c.mu.Lock() for k, v := range c.items { // "Inlining" of expired - if v.Expiration.Sec > 0 && (now.Sec > v.Expiration.Sec || (now.Sec == v.Expiration.Sec && now.Usec > v.Expiration.Usec)) { + if v.Expiration > 0 && now > v.Expiration { ov, evicted := c.delete(k) if evicted { evictedItems = append(evictedItems, keyAndValue{k, ov}) diff --git a/cache_test.go b/cache_test.go index 62b2854..f604794 100644 --- a/cache_test.go +++ b/cache_test.go @@ -6,7 +6,6 @@ import ( "runtime" "strconv" "sync" - "syscall" "testing" "time" ) @@ -111,11 +110,11 @@ func TestNewFrom(t *testing.T) { m := map[string]Item{ "a": Item{ Object: 1, - Expiration: syscall.Timeval{}, + Expiration: 0, }, "b": Item{ Object: 2, - Expiration: syscall.Timeval{}, + Expiration: 0, }, } tc := NewFrom(DefaultExpiration, 0, m)