authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-04-18 13:26:12+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-18 15:48:32-04:00
log6c907a350989aa881cc0b0f71ec81d1ae1b449b7
tree256a6c8a1e2ece8823cd2a05252a2ab6a40f11f0
parent44ff55091f1e851484e04e87d1f36d6662e80af0

std: Introduce the Once synchronization primitive

The Once object allows the user to execute a function just once in a thread-safe way.

2 files changed, 67 insertions(+), 0 deletions(-)

lib/std/once.zig created+66
......@@ -0,0 +1,66 @@
1const std = @import("std.zig");
2const builtin = std.builtin;
3const testing = std.testing;
4
5pub fn once(comptime f: fn () void) Once(f) {
6 return Once(f){};
7}
8
9/// An object that executes the function `f` just once.
10pub fn Once(comptime f: fn () void) type {
11 return struct {
12 done: bool = false,
13 mutex: std.Mutex = std.Mutex.init(),
14
15 /// Call the function `f`.
16 /// If `call` is invoked multiple times `f` will be executed only the
17 /// first time.
18 /// The invocations are thread-safe.
19 pub fn call(self: *@This()) void {
20 if (@atomicLoad(bool, &self.done, .Acquire))
21 return;
22
23 return self.callSlow();
24 }
25
26 fn callSlow(self: *@This()) void {
27 @setCold(true);
28
29 const T = self.mutex.acquire();
30 defer T.release();
31
32 // The first thread to acquire the mutex gets to run the initializer
33 if (!self.done) {
34 f();
35 @atomicStore(bool, &self.done, true, .Release);
36 }
37 }
38 };
39}
40
41var global_number: i32 = 0;
42var global_once = once(incr);
43
44fn incr() void {
45 global_number += 1;
46}
47
48test "Once executes its function just once" {
49 if (builtin.single_threaded) {
50 global_once.call();
51 global_once.call();
52 } else {
53 var threads: [10]*std.Thread = undefined;
54 defer for (threads) |handle| handle.wait();
55
56 for (threads) |*handle| {
57 handle.* = try std.Thread.spawn(@as(u8, 0), struct {
58 fn thread_fn(x: u8) void {
59 global_once.call();
60 }
61 }.thread_fn);
62 }
63 }
64
65 testing.expectEqual(@as(i32, 1), global_number);
66}
lib/std/std.zig+1
......@@ -52,6 +52,7 @@ pub const mem = @import("mem.zig");
5252pub const meta = @import("meta.zig");
5353pub const net = @import("net.zig");
5454pub const os = @import("os.zig");
55pub const once = @import("once.zig").once;
5556pub const packed_int_array = @import("packed_int_array.zig");
5657pub const pdb = @import("pdb.zig");
5758pub const process = @import("process.zig");