-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathConcurrency_test.go
More file actions
141 lines (125 loc) · 2.88 KB
/
Copy pathConcurrency_test.go
File metadata and controls
141 lines (125 loc) · 2.88 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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 MaIII Themd
package dijkstra
import (
"sync"
"sync/atomic"
"testing"
)
// TestConcurrentSearches runs many DijkstraSearch goroutines on the
// same graph and asserts every search returns the same correct path.
// Run with `go test -race` to catch any data race that would slip
// past the lock.
func TestConcurrentSearches(t *testing.T) {
g := buildSimpleGraph()
const goroutines = 64
const perGoroutine = 50
var wg sync.WaitGroup
var failures int64
for i := 0; i < goroutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < perGoroutine; j++ {
ok, path := g.DijkstraSearch("A", "D")
if !ok || len(path) != 4 {
atomic.AddInt64(&failures, 1)
return
}
want := []string{"A", "B", "C", "D"}
for k, p := range path {
if p.Name != want[k] {
atomic.AddInt64(&failures, 1)
return
}
}
if path[len(path)-1].Cost != 4 {
atomic.AddInt64(&failures, 1)
return
}
}
}()
}
wg.Wait()
if failures > 0 {
t.Fatalf("%d/%d searches returned a wrong result",
failures, int64(goroutines)*int64(perGoroutine))
}
}
// TestConcurrentReadsDuringSearch verifies that pure read methods
// remain safe to call concurrently while searches are also running.
// Useful primarily as a -race check.
func TestConcurrentReadsDuringSearch(t *testing.T) {
g := buildSimpleGraph()
stop := make(chan struct{})
var searchers sync.WaitGroup
// Long-running searchers, bounded by the stop channel.
for i := 0; i < 4; i++ {
searchers.Add(1)
go func() {
defer searchers.Done()
for {
select {
case <-stop:
return
default:
}
g.DijkstraSearch("A", "D")
}
}()
}
// Fixed-work readers.
var readers sync.WaitGroup
for i := 0; i < 8; i++ {
readers.Add(1)
go func() {
defer readers.Done()
for j := 0; j < 500; j++ {
_ = g.VertexFind("C")
_ = g.VertexIsExist("D")
_, _ = g.NearPoint(2.1, 0)
}
}()
}
readers.Wait()
close(stop)
searchers.Wait()
}
// TestConcurrentMutationAndSearch verifies that VertexBLock and
// VertexBLockRemove can run concurrently with searches without
// data races. The point of this test is that the race detector
// stays quiet -- we don't assert path correctness because the
// blocked set is being toggled mid-flight.
func TestConcurrentMutationAndSearch(t *testing.T) {
g := buildSimpleGraph()
stop := make(chan struct{})
var searchers sync.WaitGroup
for i := 0; i < 4; i++ {
searchers.Add(1)
go func() {
defer searchers.Done()
for {
select {
case <-stop:
return
default:
}
g.DijkstraSearch("A", "D")
}
}()
}
var mutators sync.WaitGroup
for i := 0; i < 4; i++ {
mutators.Add(1)
go func() {
defer mutators.Done()
for j := 0; j < 500; j++ {
g.VertexBLock("C")
g.VertexBLockRemove("C")
}
}()
}
mutators.Wait()
close(stop)
searchers.Wait()
}