mirror of
https://github.com/ziglang/zig.git
synced 2024-11-30 09:02:32 +00:00
008b0ec5e5
This is a breaking change. Before, usage looked like this: ```zig const held = mutex.acquire(); defer held.release(); ``` Now it looks like this: ```zig mutex.lock(); defer mutex.unlock(); ``` The `Held` type was an idea to make mutexes slightly safer by making it more difficult to forget to release an aquired lock. However, this ultimately caused more problems than it solved, when any data structures needed to store a held mutex. Simplify everything by reducing the API down to the primitives: lock() and unlock(). Closes #8051 Closes #8246 Closes #10105
68 lines
1.7 KiB
Zig
68 lines
1.7 KiB
Zig
const std = @import("std.zig");
|
|
const builtin = @import("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.Thread.Mutex = std.Thread.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);
|
|
|
|
self.mutex.lock();
|
|
defer self.mutex.unlock();
|
|
|
|
// 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.join();
|
|
|
|
for (threads) |*handle| {
|
|
handle.* = try std.Thread.spawn(.{}, struct {
|
|
fn thread_fn(x: u8) void {
|
|
_ = x;
|
|
global_once.call();
|
|
}
|
|
}.thread_fn, .{0});
|
|
}
|
|
}
|
|
|
|
try testing.expectEqual(@as(i32, 1), global_number);
|
|
}
|