-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmemory_race_test.go
More file actions
59 lines (48 loc) · 1.33 KB
/
memory_race_test.go
File metadata and controls
59 lines (48 loc) · 1.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//go:build !race
package fido
import (
"sync"
"sync/atomic"
"testing"
"time"
)
// Tests with concurrent cache access are skipped under race detector.
// The seqlock value storage uses intentional "benign races" that the
// race detector cannot understand.
func TestCache_Fetch_CacheHitDuringSingleflight(t *testing.T) {
cache := New[string, int](Size(1000))
var wg sync.WaitGroup
loaderCalls := atomic.Int32{}
// Start first loader that's slow
wg.Go(func() {
if _, err := cache.Fetch("key1", func() (int, error) {
loaderCalls.Add(1)
// While loader is running, another goroutine populates cache
time.Sleep(100 * time.Millisecond)
return 42, nil
}); err != nil {
t.Errorf("Fetch error: %v", err)
}
})
// Let first goroutine start and enter singleflight
time.Sleep(10 * time.Millisecond)
// While first is waiting, directly set the value in cache
cache.Set("key1", 99)
// Start second loader that should wait for first
wg.Go(func() {
val, err := cache.Fetch("key1", func() (int, error) {
loaderCalls.Add(1)
return 77, nil
})
if err != nil {
t.Errorf("Fetch error: %v", err)
return
}
// Second should get either 99 (from cache) or 42 (from first loader)
if val != 99 && val != 42 {
t.Errorf("unexpected value: %d", val)
}
})
wg.Wait()
t.Logf("loader calls: %d", loaderCalls.Load())
}