Commit 6c907a3509

LemonBoy <thatlemon@gmail.com>
2020-04-18 13:26:12
std: Introduce the Once synchronization primitive
The Once object allows the user to execute a function just once in a thread-safe way.
1 parent 44ff550
Changed files (2)
lib/std/once.zig
@@ -0,0 +1,66 @@
+const std = @import("std.zig");
+const builtin = std.builtin;
+const testing = std.testing;
+
+pub fn once(comptime f: fn () void) Once(f) {
+    return Once(f){};
+}
+
+/// An object that executes the function `f` just once.
+pub fn Once(comptime f: fn () void) type {
+    return struct {
+        done: bool = false,
+        mutex: std.Mutex = std.Mutex.init(),
+
+        /// Call the function `f`.
+        /// If `call` is invoked multiple times `f` will be executed only the
+        /// first time.
+        /// The invocations are thread-safe.
+        pub fn call(self: *@This()) void {
+            if (@atomicLoad(bool, &self.done, .Acquire))
+                return;
+
+            return self.callSlow();
+        }
+
+        fn callSlow(self: *@This()) void {
+            @setCold(true);
+
+            const T = self.mutex.acquire();
+            defer T.release();
+
+            // The first thread to acquire the mutex gets to run the initializer
+            if (!self.done) {
+                f();
+                @atomicStore(bool, &self.done, true, .Release);
+            }
+        }
+    };
+}
+
+var global_number: i32 = 0;
+var global_once = once(incr);
+
+fn incr() void {
+    global_number += 1;
+}
+
+test "Once executes its function just once" {
+    if (builtin.single_threaded) {
+        global_once.call();
+        global_once.call();
+    } else {
+        var threads: [10]*std.Thread = undefined;
+        defer for (threads) |handle| handle.wait();
+
+        for (threads) |*handle| {
+            handle.* = try std.Thread.spawn(@as(u8, 0), struct {
+                fn thread_fn(x: u8) void {
+                    global_once.call();
+                }
+            }.thread_fn);
+        }
+    }
+
+    testing.expectEqual(@as(i32, 1), global_number);
+}
lib/std/std.zig
@@ -52,6 +52,7 @@ pub const mem = @import("mem.zig");
 pub const meta = @import("meta.zig");
 pub const net = @import("net.zig");
 pub const os = @import("os.zig");
+pub const once = @import("once.zig").once;
 pub const packed_int_array = @import("packed_int_array.zig");
 pub const pdb = @import("pdb.zig");
 pub const process = @import("process.zig");