From 1cb4613b7e6273babcad8696e66505d159abae54 Mon Sep 17 00:00:00 2001 From: bakape Date: Fri, 12 May 2017 22:32:24 +0300 Subject: [PATCH] cache: Fix panics --- cache/cache.go | 4 ---- cache/native/native.go | 8 ++++---- cache/native/native_test.go | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 cache/native/native_test.go diff --git a/cache/cache.go b/cache/cache.go index 98eab0de..c16a16d9 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -7,8 +7,6 @@ import ( "github.com/ewhal/nyaa/common" "github.com/ewhal/nyaa/config" "github.com/ewhal/nyaa/model" - - "errors" ) // Cache defines interface for caching search results @@ -17,8 +15,6 @@ type Cache interface { ClearAll() } -var ErrInvalidCacheDialect = errors.New("invalid cache dialect") - // Impl cache implementation instance var Impl Cache diff --git a/cache/native/native.go b/cache/native/native.go index c9b3ba36..56c1094c 100644 --- a/cache/native/native.go +++ b/cache/native/native.go @@ -111,7 +111,9 @@ func (n *NativeCache) updateUsedSize(delta int) { } s := n.ll.Remove(e).(*store) delete(n.cache, s.key) + s.Lock() n.totalUsed -= s.size + s.Unlock() } } @@ -136,8 +138,6 @@ func (s *store) update(data []model.Torrent, count int) { s.size = newSize s.lastFetched = time.Now() - // Technically it is possible to update the size even when the store is - // already evicted, but that should never happen, unless you have a very - // small cache, very large stored datasets and a lot of traffic. - s.n.updateUsedSize(delta) + // In a separate goroutine, to ensure there is never any lock intersection + go s.n.updateUsedSize(delta) } diff --git a/cache/native/native_test.go b/cache/native/native_test.go new file mode 100644 index 00000000..6c977ed4 --- /dev/null +++ b/cache/native/native_test.go @@ -0,0 +1,37 @@ +package native + +import ( + "sync" + "testing" + + "github.com/ewhal/nyaa/common" + "github.com/ewhal/nyaa/model" +) + +// Basic test for deadlocks and race conditions +func TestConcurrency(t *testing.T) { + c := New(0.000001) + + fn := func() ([]model.Torrent, int, error) { + return []model.Torrent{{}, {}, {}}, 10, nil + } + + var wg sync.WaitGroup + wg.Add(300) + for i := 0; i < 3; i++ { + go func() { + for j := 0; j < 100; j++ { + go func(j int) { + defer wg.Done() + k := common.SearchParam{ + Page: j, + } + if _, _, err := c.Get(k, fn); err != nil { + t.Fatal(err) + } + }(j) + } + }() + } + wg.Wait() +}