| // Copyright 2022 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package bcache |
| |
| import ( |
| "fmt" |
| "runtime" |
| "sync" |
| "sync/atomic" |
| "testing" |
| ) |
| |
| var registeredCache Cache[int, int32] |
| |
| func init() { |
| registeredCache.Register() |
| } |
| |
| var seq atomic.Uint32 |
| |
| func next[T int | int32]() *T { |
| x := new(T) |
| *x = T(seq.Add(1)) |
| return x |
| } |
| |
| func str[T int | int32](x *T) string { |
| if x == nil { |
| return "nil" |
| } |
| return fmt.Sprint(*x) |
| } |
| |
| func TestCache(t *testing.T) { |
| // Use unregistered cache for functionality tests, |
| // to keep the runtime from clearing behind our backs. |
| c := new(Cache[int, int32]) |
| |
| // Create many entries. |
| m := make(map[*int]*int32) |
| for i := 0; i < 10000; i++ { |
| k := next[int]() |
| v := next[int32]() |
| m[k] = v |
| c.Put(k, v) |
| } |
| |
| // Overwrite a random 20% of those. |
| n := 0 |
| for k := range m { |
| v := next[int32]() |
| m[k] = v |
| c.Put(k, v) |
| if n++; n >= 2000 { |
| break |
| } |
| } |
| |
| // Check results. |
| for k, v := range m { |
| if cv := c.Get(k); cv != v { |
| t.Fatalf("c.Get(%v) = %v, want %v", str(k), str(cv), str(v)) |
| } |
| } |
| |
| c.Clear() |
| for k := range m { |
| if cv := c.Get(k); cv != nil { |
| t.Fatalf("after GC, c.Get(%v) = %v, want nil", str(k), str(cv)) |
| } |
| } |
| |
| // Check that registered cache is cleared at GC. |
| c = ®isteredCache |
| for k, v := range m { |
| c.Put(k, v) |
| } |
| runtime.GC() |
| for k := range m { |
| if cv := c.Get(k); cv != nil { |
| t.Fatalf("after Clear, c.Get(%v) = %v, want nil", str(k), str(cv)) |
| } |
| } |
| |
| // Check that cache works for concurrent access. |
| // Lists are discarded if they reach 1000 entries, |
| // and there are cacheSize list heads, so we should be |
| // able to do 100 * cacheSize entries with no problem at all. |
| c = new(Cache[int, int32]) |
| var barrier, wg sync.WaitGroup |
| const N = 100 |
| barrier.Add(N) |
| wg.Add(N) |
| var lost int32 |
| for i := 0; i < N; i++ { |
| go func() { |
| defer wg.Done() |
| |
| m := make(map[*int]*int32) |
| for j := 0; j < cacheSize; j++ { |
| k, v := next[int](), next[int32]() |
| m[k] = v |
| c.Put(k, v) |
| } |
| barrier.Done() |
| barrier.Wait() |
| |
| for k, v := range m { |
| if cv := c.Get(k); cv != v { |
| t.Errorf("c.Get(%v) = %v, want %v", str(k), str(cv), str(v)) |
| atomic.AddInt32(&lost, +1) |
| } |
| } |
| }() |
| } |
| wg.Wait() |
| if lost != 0 { |
| t.Errorf("lost %d entries", lost) |
| } |
| } |