1 | module sync |
2 | |
3 | import sync.stdatomic |
4 | |
5 | pub struct Once { |
6 | mut: |
7 | m RwMutex |
8 | pub: |
9 | count u64 |
10 | } |
11 | |
12 | // new_once return a new Once struct. |
13 | pub fn new_once() &Once { |
14 | mut once := &Once{} |
15 | once.m.init() |
16 | return once |
17 | } |
18 | |
19 | // do executes the function `f()` only once |
20 | pub fn (mut o Once) do(f fn ()) { |
21 | if stdatomic.load_u64(&o.count) < 1 { |
22 | o.do_slow(f) |
23 | } |
24 | } |
25 | |
26 | fn (mut o Once) do_slow(f fn ()) { |
27 | o.m.@lock() |
28 | if o.count < 1 { |
29 | stdatomic.store_u64(&o.count, 1) |
30 | f() |
31 | } |
32 | o.m.unlock() |
33 | } |
34 | |
35 | // do_with_param executes `f(param)` only once` |
36 | // This method can be used as a workaround for passing closures to once.do/1 on Windows |
37 | // (they are not implemented there yet) - just pass your data explicitly. |
38 | // i.e. instead of: |
39 | // ```v |
40 | // once.do(fn [mut o] () { |
41 | // o.add(5) |
42 | // }) |
43 | // ``` |
44 | // |
45 | // ... you can use: |
46 | // ```v |
47 | // once.do_with_param(fn (mut o One) { |
48 | // o.add(5) |
49 | // }, o) |
50 | // ``` |
51 | |
52 | pub fn (mut o Once) do_with_param(f fn (voidptr), param voidptr) { |
53 | if stdatomic.load_u64(&o.count) < 1 { |
54 | o.do_slow_with_param(f, param) |
55 | } |
56 | } |
57 | |
58 | fn (mut o Once) do_slow_with_param(f fn (p voidptr), param voidptr) { |
59 | o.m.@lock() |
60 | if o.count < 1 { |
61 | stdatomic.store_u64(&o.count, 1) |
62 | f(param) |
63 | } |
64 | o.m.unlock() |
65 | } |