From 0aa08303bc0730cb48b8c4b20c063512940db878 Mon Sep 17 00:00:00 2001 From: Leon Klingele Date: Mon, 14 Nov 2022 03:18:42 +0100 Subject: [PATCH 1/3] memory: cache timestamp --- memory/memory.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/memory/memory.go b/memory/memory.go index b1bd2f4e..bb4f0b00 100644 --- a/memory/memory.go +++ b/memory/memory.go @@ -110,10 +110,11 @@ func (s *Storage) gc() { case <-s.done: return case <-ticker.C: + ts := atomic.LoadUint32(&internal.Timestamp) expired = expired[:0] s.mux.RLock() for id, v := range s.db { - if v.expiry != 0 && v.expiry < atomic.LoadUint32(&internal.Timestamp) { + if v.expiry != 0 && v.expiry < ts { expired = append(expired, id) } } From 318b2fc04c418618bedc0e61425a1dcb107bc666 Mon Sep 17 00:00:00 2001 From: Leon Klingele Date: Mon, 14 Nov 2022 03:19:31 +0100 Subject: [PATCH 2/3] memory: ensure to never delete non-expired items This fixes a TOCTOU problem between a mutex rlock and a mutex lock. --- memory/memory.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/memory/memory.go b/memory/memory.go index bb4f0b00..49b1a117 100644 --- a/memory/memory.go +++ b/memory/memory.go @@ -120,8 +120,13 @@ func (s *Storage) gc() { } s.mux.RUnlock() s.mux.Lock() + // Double-checked locking. + // We might have replaced the item in the meantime. for i := range expired { - delete(s.db, expired[i]) + v := s.db[expired[i]] + if v.expiry != 0 && v.expiry <= ts { + delete(s.db, expired[i]) + } } s.mux.Unlock() } From 117838fd0eb60b1dfb3e58670023b0562ab27919 Mon Sep 17 00:00:00 2001 From: Leon Klingele Date: Mon, 14 Nov 2022 03:20:20 +0100 Subject: [PATCH 3/3] memory: move costly operations outside of locked area --- memory/memory.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/memory/memory.go b/memory/memory.go index 49b1a117..3acf1ec6 100644 --- a/memory/memory.go +++ b/memory/memory.go @@ -68,8 +68,9 @@ func (s *Storage) Set(key string, val []byte, exp time.Duration) error { expire = uint32(exp.Seconds()) + atomic.LoadUint32(&internal.Timestamp) } + e := entry{val, expire} s.mux.Lock() - s.db[key] = entry{val, expire} + s.db[key] = e s.mux.Unlock() return nil } @@ -88,8 +89,9 @@ func (s *Storage) Delete(key string) error { // Reset all keys func (s *Storage) Reset() error { + ndb := make(map[string]entry) s.mux.Lock() - s.db = make(map[string]entry) + s.db = ndb s.mux.Unlock() return nil }