blob: 3ba4bd4a70d3a643749ec9f775a6e2c9ebb6d243 (
plain)
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
60
61
62
63
64
65
66
67
68
69
70
|
package game
// Single-owner serialization regressions: a full task queue applies
// backpressure instead of bypassing the owner, and the owner-stop path
// keeps its documented inline fallback for shutdown stragglers only.
import (
"sync"
"testing"
"time"
)
// TestFullOwnerQueueAppliesBackpressure: when the owner's task queue is
// full and the owner is blocked inside another task, run() must NOT execute
// the new task inline on the caller's goroutine — that would mutate live
// state concurrently with the owner. It blocks until the owner drains.
func TestFullOwnerQueueAppliesBackpressure(t *testing.T) {
g := &Game{owner: newOwner()}
release := make(chan struct{})
started := make(chan struct{})
g.owner.submit(func() {
close(started)
<-release
})
<-started
var once sync.Once
t.Cleanup(func() {
once.Do(func() { close(release) })
g.stopOwner()
})
for i := 0; i < ownerTaskQueue; i++ {
if !g.owner.submit(func() {}) {
t.Fatal("setup: could not fill the owner queue")
}
}
ran := make(chan struct{})
go g.run(func() { close(ran) })
select {
case <-ran:
t.Fatal("task executed inline while the owner was still running other tasks")
case <-time.After(100 * time.Millisecond):
}
// Release the owner: the queued work drains and the blocked task runs.
once.Do(func() { close(release) })
select {
case <-ran:
case <-time.After(5 * time.Second):
t.Fatal("task never ran after the owner drained its queue")
}
}
// TestStoppedOwnerDropsStragglers: after the owner stops (graceful
// shutdown), straggler submissions are dropped, never executed inline on the
// caller's goroutine (which would mutate live state concurrently with the
// shutdown sequence and after the final-save barrier) and never block
// forever.
func TestStoppedOwnerDropsStragglers(t *testing.T) {
g := &Game{owner: newOwner()}
g.stopOwner()
ran := make(chan struct{})
go g.run(func() { close(ran) })
select {
case <-ran:
t.Fatal("straggler task executed after the owner stopped; it must be dropped")
case <-time.After(100 * time.Millisecond):
}
}
|