v / vlib / sync / cond_test.v
191 lines · 163 sloc · 4.27 KB · d6a2a5e9259f5d3fa54aa7a4c38378accaa9df94
Raw
1import sync
2import sync.stdatomic { new_atomic }
3
4// Test single thread wake-up scenario for condition variable
5fn test_single_thread_wakeup() {
6 mut mutex := sync.new_mutex()
7 mut cond := sync.new_cond(mutex)
8 mut done := new_atomic(false)
9 mut wake_count := new_atomic(0)
10 ready_ch := chan bool{cap: 1}
11 done_ch := chan bool{cap: 1}
12 defer {
13 ready_ch.close()
14 done_ch.close()
15 }
16
17 // Spawn waiting thread
18 spawn fn [mut done, mut wake_count, mut cond, mut mutex, ready_ch, done_ch] () {
19 mutex.lock()
20 defer {
21 mutex.unlock()
22 }
23 ready_ch <- true // Notify main thread of readiness
24
25 // Wait loop for conditional signals
26 for !done.load() {
27 cond.wait()
28 wake_count.add(1)
29 }
30 done_ch <- true // Notify completion
31 }()
32
33 // Wait for the worker to enter waiting state
34 _ := <-ready_ch
35
36 // Trigger signaling sequence
37 mutex.lock()
38 cond.signal() // Wake the waiting thread
39 done.store(true) // Terminate worker loop
40 cond.signal() // Extra signal for loop exit
41 mutex.unlock()
42
43 // Verify result
44 _ := <-done_ch
45 assert wake_count.load() == 1, 'Should wake exactly 1 thread'
46}
47
48// Test broadcast wake-up of multiple waiting threads
49fn test_broadcast_wakeup() {
50 mut mutex := sync.new_mutex()
51 mut cond := sync.new_cond(mutex)
52 num_threads := 5
53 mut wake_counter := new_atomic(0)
54 ready_ch := chan bool{cap: num_threads}
55 done_ch := chan bool{cap: num_threads}
56 defer {
57 ready_ch.close()
58 done_ch.close()
59 }
60
61 // Spawn multiple waiting threads
62 for _ in 0 .. num_threads {
63 spawn fn [mut wake_counter, mut cond, mut mutex, ready_ch, done_ch] () {
64 mutex.lock()
65 defer {
66 mutex.unlock()
67 }
68 ready_ch <- true // Notify readiness
69 cond.wait() // Wait for broadcast
70 wake_counter.add(1)
71 done_ch <- true // Notify completion
72 }()
73 }
74
75 // Wait for all threads to enter waiting state
76 for _ in 0 .. num_threads {
77 _ := <-ready_ch
78 }
79
80 // Trigger broadcast wake-up
81 mutex.lock()
82 cond.broadcast()
83 mutex.unlock()
84
85 // Verify all threads completed
86 for _ in 0 .. num_threads {
87 _ := <-done_ch
88 }
89 assert wake_counter.load() == num_threads, 'Should wake all threads'
90}
91
92// Test consecutive signal delivery sequencing
93fn test_multiple_signals() {
94 mut mutex := sync.new_mutex()
95 mut cond := sync.new_cond(mutex)
96 mut counter := new_atomic(0)
97 num_signals := 3
98 ready_ch := chan bool{cap: 1}
99 wait_sync_ch := chan bool{cap: 1} // Synchronization for wait-sequence tracking
100 done_ch := chan bool{cap: 1}
101 defer {
102 ready_ch.close()
103 wait_sync_ch.close()
104 done_ch.close()
105 }
106
107 spawn fn [num_signals, mut counter, mut cond, mut mutex, ready_ch, wait_sync_ch, done_ch] () {
108 mutex.lock()
109 defer {
110 mutex.unlock()
111 }
112
113 ready_ch <- true // Initial readiness notification
114
115 // Process multiple signals sequentially
116 for _ in 0 .. num_signals {
117 cond.wait()
118 counter.add(1)
119 wait_sync_ch <- true // Signal processing complete
120 }
121 done_ch <- true
122 }()
123
124 // Wait for initial setup
125 _ := <-ready_ch
126
127 // Send first signal
128 mutex.lock()
129 cond.signal()
130 mutex.unlock()
131
132 // Send subsequent signals with synchronization
133 for _ in 1 .. num_signals {
134 _ := <-wait_sync_ch // Wait for previous signal processing
135 mutex.lock()
136 cond.signal()
137 mutex.unlock()
138 }
139
140 _ := <-done_ch
141 assert counter.load() == num_signals, 'Signal count should match counter value'
142}
143
144// Test lock reacquisition mechanics after wait()
145fn test_lock_reacquire() {
146 mut mutex := sync.new_mutex()
147 mut cond := sync.new_cond(mutex)
148 mut lock_held := new_atomic(false)
149 ready_ch := chan bool{cap: 1}
150 done_ch := chan bool{cap: 1}
151 defer {
152 ready_ch.close()
153 done_ch.close()
154 }
155
156 spawn fn [mut lock_held, mut cond, mut mutex, ready_ch, done_ch] () {
157 mutex.lock()
158 defer {
159 mutex.unlock()
160 }
161 ready_ch <- true
162
163 cond.wait()
164 // Test lock state after wakeup
165 lock_held.store(!mutex.try_lock()) // Should fail -> store true
166 done_ch <- true
167 }()
168
169 _ := <-ready_ch
170
171 mutex.lock()
172 cond.signal()
173 mutex.unlock()
174
175 _ := <-done_ch
176 assert lock_held.load(), 'Mutex should be reacquired automatically after wait()'
177}
178
179// Test empty signal/broadcast scenario
180fn test_signal_without_waiters() {
181 mut mutex := sync.new_mutex()
182 mut cond := sync.new_cond(mutex)
183
184 // Verify no panic occurs
185 mutex.lock()
186 cond.signal() // No-op with no waiters
187 cond.broadcast() // No-op with no waiters
188 mutex.unlock()
189
190 assert true, 'Should handle empty signal operations safely'
191}
192