vxx2 / vlib / sync / once_with_param_test.v
85 lines · 71 sloc · 2.06 KB · df24ab237ef04ddaf535d9b7253cfa8773771816
Raw
1import sync
2import time
3
4// Note: this is the same test as `vlib/sync/once_test.v`, but
5// it uses an explicit passing of the voidptr parameter in
6// once.do_with_param/2, instead of passing a closure of it
7// in once.do/1.
8
9struct One {
10pub mut:
11 i int
12}
13
14fn (mut o One) add(i int) {
15 o.i = o.i + i
16}
17
18fn run(mut once sync.Once, mut o One, c chan bool) {
19 once.do_with_param(fn (mut o One) {
20 o.add(5)
21 }, o)
22 c <- true
23}
24
25fn test_once() {
26 mut o := &One{}
27 mut once := sync.new_once()
28 c := chan bool{}
29 n := 10
30
31 // It is executed 10 times, but only once actually.
32 for i := 0; i < n; i++ {
33 spawn run(mut once, mut o, c)
34 }
35 for i := 0; i < n; i++ {
36 <-c
37 }
38 assert o.i == 5
39}
40
41// Ordering test: do_with_param must not return until f() has completed.
42// The bug was that count was set to 1 *before* f() ran, so a concurrent
43// caller could observe count==1 and return on the fast path while f() was
44// still executing (or hadn't started).
45
46struct OrderingState {
47pub mut:
48 value int
49 ready chan bool
50}
51
52fn slow_init(p voidptr) {
53 mut s := unsafe { &OrderingState(p) }
54 // Signal that we are inside f() — count has been stored (buggy) or not (fixed).
55 s.ready <- true
56 // Sleep long enough for the concurrent do_with_param call to observe the state.
57 time.sleep(50 * time.millisecond)
58 s.value = 99
59}
60
61fn run_ordering(mut once sync.Once, s &OrderingState, c chan bool) {
62 once.do_with_param(slow_init, s)
63 c <- true
64}
65
66fn test_once_with_param_ordering() {
67 mut s := &OrderingState{
68 ready: chan bool{cap: 1}
69 }
70 mut once := sync.new_once()
71 c := chan bool{}
72
73 spawn run_ordering(mut once, s, c)
74
75 // Wait until the goroutine is inside f() before letting the second
76 // caller proceed. With the old buggy code count==1 is already visible
77 // here, so the second do_with_param returns immediately — before
78 // s.value is set. With the fix, count is still 0, so the second
79 // caller blocks on the mutex and waits for f() to complete.
80 _ := <-s.ready
81 once.do_with_param(fn (p voidptr) {}, unsafe { nil })
82
83 assert s.value == 99, 'do_with_param must not return before f() completes'
84 _ := <-c
85}
86