vxx2 / vlib / x / executor / sync_call_test.v
319 lines · 282 sloc · 7.75 KB · e80386b6af8a071a2e13fd654ebcdb2105fb9202
Raw
1module executor
2
3import time
4
5fn test_run_sync_from_foreign_thread_waits_until_owner_executes_job() {
6 mut ex := new(queue_size: 4)!
7 sync_started := chan bool{cap: 1}
8 sync_ran := chan bool{cap: 1}
9 sync_done := chan bool{cap: 1}
10 run_result := chan string{cap: 1}
11
12 runner := spawn fn [mut ex, run_result] () {
13 ex.run() or {
14 run_result <- err.msg()
15 return
16 }
17 run_result <- 'ok'
18 }()
19
20 caller := spawn fn [mut ex, sync_started, sync_ran, sync_done] () {
21 sync_started <- true
22 ex.run_sync(fn [sync_ran] () ! {
23 sync_ran <- true
24 }) or {
25 sync_done <- false
26 return
27 }
28 sync_done <- true
29 }()
30
31 assert wait_for_bool(sync_started, 'run_sync caller did not start')
32 assert wait_for_bool(sync_ran, 'run_sync job did not run on owner pump')
33 assert wait_for_bool(sync_done, 'run_sync caller did not finish')
34 ex.stop()
35 assert wait_for_string(run_result, 'run did not stop') == 'ok'
36 ex.wait()!
37 caller.wait()
38 runner.wait()
39}
40
41fn test_run_sync_returns_job_error_to_foreign_caller() {
42 mut ex := new(queue_size: 2)!
43 run_result := chan string{cap: 1}
44 sync_result := chan string{cap: 1}
45
46 runner := spawn fn [mut ex, run_result] () {
47 ex.run() or {
48 run_result <- err.msg()
49 return
50 }
51 run_result <- 'ok'
52 }()
53
54 caller := spawn fn [mut ex, sync_result] () {
55 ex.run_sync(fn () ! {
56 return error('sync job failed')
57 }) or {
58 sync_result <- err.msg()
59 return
60 }
61 sync_result <- 'ok'
62 }()
63
64 assert wait_for_string(sync_result, 'run_sync error was not returned') == 'sync job failed'
65 assert wait_for_string(run_result, 'run did not return after sync error') == 'sync job failed'
66 ex.wait() or { assert err.msg() == 'sync job failed' }
67 caller.wait()
68 runner.wait()
69}
70
71fn test_run_sync_from_owner_runs_inline_without_deadlock() {
72 mut ex := new(queue_size: 4)!
73 events := chan int{cap: 4}
74
75 ex.try_post(fn [mut ex, events] () ! {
76 events <- 1
77 ex.run_sync(fn [events] () ! {
78 events <- 100
79 })!
80 events <- 2
81 })!
82 ex.try_post(fn [events] () ! {
83 events <- 3
84 })!
85
86 assert ex.drain_pending(2)! == 2
87 assert wait_for_int(events, 'outer job did not start') == 1
88 assert wait_for_int(events, 'inline run_sync job did not run') == 100
89 assert wait_for_int(events, 'outer job did not finish') == 2
90 assert wait_for_int(events, 'second queued job did not run') == 3
91
92 ex.stop()
93 assert !ex.run_one()!
94 ex.wait()!
95}
96
97fn test_run_sync_owner_inline_error_caught_closes_admission_and_wait_returns_error() {
98 mut ex := new(queue_size: 4)!
99 events := chan int{cap: 3}
100 post_result := chan string{cap: 1}
101 run_result := chan string{cap: 1}
102
103 ex.try_post(fn [mut ex, events, post_result] () ! {
104 events <- 1
105 ex.run_sync(fn () ! {
106 return error('inline sync failed')
107 }) or { events <- 2 }
108 ex.try_post(fn () ! {}) or {
109 post_result <- err.msg()
110 return
111 }
112 post_result <- 'accepted'
113 ex.stop()
114 })!
115 ex.try_post(fn [events] () ! {
116 events <- 3
117 })!
118
119 runner := spawn fn [mut ex, run_result] () {
120 ex.run() or {
121 run_result <- err.msg()
122 return
123 }
124 run_result <- 'ok'
125 }()
126
127 assert wait_for_int(events, 'outer job did not start') == 1
128 assert wait_for_int(events, 'inline run_sync error was not caught') == 2
129 assert wait_for_string(post_result, 'try_post after inline error did not return') == 'executor: executor is closed'
130 assert wait_for_int(events, 'accepted job after inline error was not drained') == 3
131 assert wait_for_string(run_result, 'run did not return inline first error') == 'inline sync failed'
132 ex.wait() or {
133 assert err.msg() == 'inline sync failed'
134 runner.wait()
135 return
136 }
137 assert false, 'wait returned successfully after inline run_sync error'
138}
139
140fn test_run_one_returns_owner_inline_run_sync_error_caught_by_outer_job() {
141 mut ex := new(queue_size: 4)!
142 events := chan int{cap: 2}
143
144 ex.try_post(fn [mut ex, events] () ! {
145 ex.run_sync(fn () ! {
146 return error('inline sync failed')
147 }) or { events <- 1 }
148 })!
149 ex.try_post(fn [events] () ! {
150 events <- 2
151 })!
152
153 mut saw_inline_error := false
154 ex.run_one() or {
155 assert err.msg() == 'inline sync failed'
156 saw_inline_error = true
157 }
158 assert saw_inline_error
159 assert wait_for_int(events, 'outer job did not catch inline run_sync error') == 1
160
161 mut saw_closed := false
162 ex.try_post(fn () ! {}) or {
163 assert err.msg() == 'executor: executor is closed'
164 saw_closed = true
165 }
166 assert saw_closed
167
168 ex.run_one() or { assert err.msg() == 'inline sync failed' }
169 assert wait_for_int(events, 'accepted job after inline error was not drained') == 2
170 ex.wait() or {
171 assert err.msg() == 'inline sync failed'
172 return
173 }
174 assert false, 'wait returned successfully after inline run_sync error'
175}
176
177fn test_drain_pending_limit_returns_owner_inline_run_sync_error_caught_by_outer_job() {
178 mut ex := new(queue_size: 4)!
179 events := chan int{cap: 2}
180
181 ex.try_post(fn [mut ex, events] () ! {
182 ex.run_sync(fn () ! {
183 return error('inline sync failed')
184 }) or { events <- 1 }
185 })!
186 ex.try_post(fn [events] () ! {
187 events <- 2
188 })!
189
190 ex.drain_pending(1) or { assert err.msg() == 'inline sync failed' }
191 assert wait_for_int(events, 'outer job did not catch inline run_sync error') == 1
192
193 ex.drain_pending(8) or { assert err.msg() == 'inline sync failed' }
194 assert wait_for_int(events, 'accepted job after inline error was not drained') == 2
195 ex.wait() or {
196 assert err.msg() == 'inline sync failed'
197 return
198 }
199 assert false, 'wait returned successfully after inline run_sync error'
200}
201
202fn test_run_sync_from_owner_after_stop_returns_closed_without_running_nested_job() {
203 mut ex := new(queue_size: 2)!
204 result := chan string{cap: 1}
205 nested_ran := chan bool{cap: 1}
206
207 ex.try_post(fn [mut ex, result, nested_ran] () ! {
208 ex.stop()
209 ex.run_sync(fn [nested_ran] () ! {
210 nested_ran <- true
211 }) or {
212 result <- err.msg()
213 return
214 }
215 result <- 'ok'
216 })!
217
218 assert ex.run_one()!
219 assert wait_for_string(result, 'owner run_sync after stop did not return') == 'executor: executor is closed'
220 select {
221 _ := <-nested_ran {
222 assert false, 'nested run_sync job ran after stop'
223 }
224 else {}
225 }
226 ex.wait()!
227}
228
229fn test_run_sync_from_owner_after_prior_job_error_is_refused_without_nested_execution() {
230 mut ex := new(queue_size: 3)!
231 result := chan string{cap: 1}
232 nested_ran := chan bool{cap: 1}
233
234 ex.try_post(fn () ! {
235 return error('first job failed')
236 })!
237 ex.try_post(fn [mut ex, result, nested_ran] () ! {
238 ex.run_sync(fn [nested_ran] () ! {
239 nested_ran <- true
240 }) or {
241 result <- err.msg()
242 return
243 }
244 result <- 'ok'
245 })!
246
247 mut run_result := 'ok'
248 ex.run() or { run_result = err.msg() }
249 assert run_result == 'first job failed'
250 assert wait_for_string(result, 'owner run_sync after first error did not return') == 'executor: executor is closed'
251 select {
252 _ := <-nested_ran {
253 assert false, 'nested run_sync job ran after first error'
254 }
255 else {}
256 }
257 ex.wait() or {
258 assert err.msg() == 'first job failed'
259 return
260 }
261 assert false, 'wait returned successfully after first job error'
262}
263
264fn test_run_sync_after_stop_returns_error() {
265 mut ex := new(queue_size: 1)!
266 ex.stop()
267 ex.run_sync(fn () ! {}) or {
268 assert err.msg() == 'executor: executor is closed'
269 return
270 }
271 assert false, 'run_sync accepted after stop'
272}
273
274fn test_run_sync_rejects_nil_job() {
275 mut ex := new(queue_size: 1)!
276 nil_job := unsafe { JobFn(nil) }
277 ex.run_sync(nil_job) or {
278 assert err.msg() == 'executor: job function is nil'
279 ex.stop()
280 return
281 }
282 assert false, 'run_sync accepted a nil job'
283}
284
285fn wait_for_bool(signal chan bool, message string) bool {
286 select {
287 value := <-signal {
288 return value
289 }
290 1 * time.second {
291 assert false, message
292 }
293 }
294 return false
295}
296
297fn wait_for_int(signal chan int, message string) int {
298 select {
299 value := <-signal {
300 return value
301 }
302 1 * time.second {
303 assert false, message
304 }
305 }
306 return 0
307}
308
309fn wait_for_string(signal chan string, message string) string {
310 select {
311 value := <-signal {
312 return value
313 }
314 1 * time.second {
315 assert false, message
316 }
317 }
318 return ''
319}
320