From dd835acb8d79e79580ab88687f2bb04124a41fbf Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Tue, 8 Feb 2022 17:15:28 +0200 Subject: [PATCH] sync: add Once.do_with_param/2 method in addition to the existing Once.do/1 (workaround the absence of closures on windows) --- vlib/sync/once.v | 33 ++++++++++++++++++++++++++- vlib/sync/once_with_param_test.v | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 vlib/sync/once_with_param_test.v diff --git a/vlib/sync/once.v b/vlib/sync/once.v index 7f2bd42687..9dff78ffc4 100644 --- a/vlib/sync/once.v +++ b/vlib/sync/once.v @@ -16,7 +16,7 @@ pub fn new_once() &Once { return once } -// do execute the function only once. +// do executes the function `f()` only once pub fn (mut o Once) do(f fn ()) { if stdatomic.load_u64(&o.count) < 1 { o.do_slow(f) @@ -31,3 +31,34 @@ fn (mut o Once) do_slow(f fn ()) { } o.m.unlock() } + +// do_with_param executes `f(param)` only once` +// This method can be used as a workaround for passing closures to once.do/1 on Windows +// (they are not implemented there yet) - just pass your data explicitly. +// i.e. instead of: +// ```v +// once.do(fn [mut o] () { +// o.add(5) +// }) +// ``` +// ... you can use: +// ```v +// once.do_with_param(fn (mut o One) { +// o.add(5) +// }, o) +// ``` + +pub fn (mut o Once) do_with_param(f fn (voidptr), param voidptr) { + if stdatomic.load_u64(&o.count) < 1 { + o.do_slow_with_param(f, param) + } +} + +fn (mut o Once) do_slow_with_param(f fn (p voidptr), param voidptr) { + o.m.@lock() + if o.count < 1 { + stdatomic.store_u64(&o.count, 1) + f(param) + } + o.m.unlock() +} diff --git a/vlib/sync/once_with_param_test.v b/vlib/sync/once_with_param_test.v new file mode 100644 index 0000000000..ededa1e698 --- /dev/null +++ b/vlib/sync/once_with_param_test.v @@ -0,0 +1,39 @@ +import sync + +// NB: this is the same test as `vlib/sync/once_test.v`, but +// it uses an explicit passing of the voidptr parameter in +// once.do_with_param/2, instead of passing a closure of it +// in once.do/1. +// Closures are not yet implemented on Windows. + +struct One { +pub mut: + i int +} + +fn (mut o One) add(i int) { + o.i = o.i + i +} + +fn run(mut once sync.Once, mut o One, c chan bool) { + once.do_with_param(fn (mut o One) { + o.add(5) + }, o) + c <- true +} + +fn test_once() { + mut o := &One{} + mut once := sync.new_once() + c := chan bool{} + n := 10 + + // It is executed 10 times, but only once actually. + for i := 0; i < n; i++ { + go run(mut once, mut o, c) + } + for i := 0; i < n; i++ { + <-c + } + assert o.i == 5 +}