* std.Mutex API is improved to not have init() deinit(). This API is designed to support static initialization and does not require any resource cleanup. This also happens to work around some kind of stage1 behavior that wasn't letting the new allocator mutex code get compiled. * the general purpose allocator now returns a bool from deinit() which tells if there were any leaks. This value is used by the test runner to fail the tests if there are any. * self-hosted compiler is updated to use the general purpose allocator when not linking against libc.
67 lines
1.7 KiB
Zig
67 lines
1.7 KiB
Zig
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{},
|
|
|
|
/// 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);
|
|
}
|