2019-03-02 13:46:04 -08:00
|
|
|
const std = @import("std.zig");
|
2019-02-01 09:22:21 -08:00
|
|
|
const builtin = @import("builtin");
|
|
|
|
const AtomicOrder = builtin.AtomicOrder;
|
|
|
|
const AtomicRmwOp = builtin.AtomicRmwOp;
|
|
|
|
const assert = std.debug.assert;
|
2019-02-08 15:18:47 -08:00
|
|
|
const expect = std.testing.expect;
|
2019-02-01 09:22:21 -08:00
|
|
|
const windows = std.os.windows;
|
|
|
|
|
|
|
|
/// Lock may be held only once. If the same thread
|
|
|
|
/// tries to acquire the same mutex twice, it deadlocks.
|
|
|
|
/// This type is intended to be initialized statically. If you don't
|
|
|
|
/// require static initialization, use std.Mutex.
|
|
|
|
/// On Windows, this mutex allocates resources when it is
|
|
|
|
/// first used, and the resources cannot be freed.
|
|
|
|
/// On Linux, this is an alias of std.Mutex.
|
2019-03-02 13:46:04 -08:00
|
|
|
pub const StaticallyInitializedMutex = switch (builtin.os) {
|
2019-02-01 09:22:21 -08:00
|
|
|
builtin.Os.linux => std.Mutex,
|
|
|
|
builtin.Os.windows => struct {
|
|
|
|
lock: windows.CRITICAL_SECTION,
|
|
|
|
init_once: windows.RTL_RUN_ONCE,
|
|
|
|
|
|
|
|
pub const Held = struct {
|
|
|
|
mutex: *StaticallyInitializedMutex,
|
|
|
|
|
|
|
|
pub fn release(self: Held) void {
|
2019-05-26 21:48:56 -07:00
|
|
|
windows.kernel32.LeaveCriticalSection(&self.mutex.lock);
|
2019-02-01 09:22:21 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn init() StaticallyInitializedMutex {
|
2019-03-02 13:46:04 -08:00
|
|
|
return StaticallyInitializedMutex{
|
2019-02-01 09:22:21 -08:00
|
|
|
.lock = undefined,
|
|
|
|
.init_once = windows.INIT_ONCE_STATIC_INIT,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
extern fn initCriticalSection(
|
|
|
|
InitOnce: *windows.RTL_RUN_ONCE,
|
|
|
|
Parameter: ?*c_void,
|
|
|
|
Context: ?*c_void,
|
|
|
|
) windows.BOOL {
|
|
|
|
const lock = @ptrCast(*windows.CRITICAL_SECTION, @alignCast(@alignOf(windows.CRITICAL_SECTION), Parameter));
|
2019-05-26 21:48:56 -07:00
|
|
|
windows.kernel32.InitializeCriticalSection(lock);
|
2019-02-01 09:22:21 -08:00
|
|
|
return windows.TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// TODO: once https://github.com/ziglang/zig/issues/287 is solved and std.Mutex has a better
|
|
|
|
/// implementation of a runtime initialized mutex, remove this function.
|
|
|
|
pub fn deinit(self: *StaticallyInitializedMutex) void {
|
2019-05-26 21:48:56 -07:00
|
|
|
windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null);
|
|
|
|
windows.kernel32.DeleteCriticalSection(&self.lock);
|
2019-02-01 09:22:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn acquire(self: *StaticallyInitializedMutex) Held {
|
2019-05-26 21:48:56 -07:00
|
|
|
windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null);
|
|
|
|
windows.kernel32.EnterCriticalSection(&self.lock);
|
2019-03-02 13:46:04 -08:00
|
|
|
return Held{ .mutex = self };
|
2019-02-01 09:22:21 -08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
else => std.Mutex,
|
|
|
|
};
|
|
|
|
|
|
|
|
test "std.StaticallyInitializedMutex" {
|
|
|
|
const TestContext = struct {
|
|
|
|
data: i128,
|
|
|
|
|
|
|
|
const TestContext = @This();
|
|
|
|
const incr_count = 10000;
|
|
|
|
|
|
|
|
var mutex = StaticallyInitializedMutex.init();
|
|
|
|
|
|
|
|
fn worker(ctx: *TestContext) void {
|
|
|
|
var i: usize = 0;
|
|
|
|
while (i != TestContext.incr_count) : (i += 1) {
|
|
|
|
const held = mutex.acquire();
|
|
|
|
defer held.release();
|
|
|
|
|
|
|
|
ctx.data += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var direct_allocator = std.heap.DirectAllocator.init();
|
|
|
|
defer direct_allocator.deinit();
|
|
|
|
|
|
|
|
var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
|
|
|
|
defer direct_allocator.allocator.free(plenty_of_memory);
|
|
|
|
|
|
|
|
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
|
|
|
|
var a = &fixed_buffer_allocator.allocator;
|
|
|
|
|
2019-03-02 13:46:04 -08:00
|
|
|
var context = TestContext{ .data = 0 };
|
2019-02-01 09:22:21 -08:00
|
|
|
|
2019-02-01 14:49:29 -08:00
|
|
|
if (builtin.single_threaded) {
|
|
|
|
TestContext.worker(&context);
|
2019-02-08 15:18:47 -08:00
|
|
|
expect(context.data == TestContext.incr_count);
|
2019-02-01 14:49:29 -08:00
|
|
|
} else {
|
|
|
|
const thread_count = 10;
|
2019-05-25 10:07:44 -07:00
|
|
|
var threads: [thread_count]*std.Thread = undefined;
|
2019-02-01 14:49:29 -08:00
|
|
|
for (threads) |*t| {
|
2019-05-25 10:07:44 -07:00
|
|
|
t.* = try std.Thread.spawn(&context, TestContext.worker);
|
2019-02-01 14:49:29 -08:00
|
|
|
}
|
|
|
|
for (threads) |t|
|
|
|
|
t.wait();
|
2019-02-01 09:22:21 -08:00
|
|
|
|
2019-02-08 15:18:47 -08:00
|
|
|
expect(context.data == thread_count * TestContext.incr_count);
|
2019-02-01 14:49:29 -08:00
|
|
|
}
|
2019-02-01 09:22:21 -08:00
|
|
|
}
|