From b61bce254cc5c7075e2fb722e7deb0085eebb65e Mon Sep 17 00:00:00 2001 From: emekoi Date: Sun, 18 Nov 2018 00:07:37 -0600 Subject: [PATCH 01/11] added mutex for windows --- std/mutex.zig | 135 +++++++++++++++++++++++++----------- std/os/windows/kernel32.zig | 31 +++++++++ 2 files changed, 127 insertions(+), 39 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index e35bd81bc4..25e6a5bd25 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -5,70 +5,125 @@ const AtomicRmwOp = builtin.AtomicRmwOp; const assert = std.debug.assert; const SpinLock = std.SpinLock; const linux = std.os.linux; +const windows = std.os.windows; /// Lock may be held only once. If the same thread /// tries to acquire the same mutex twice, it deadlocks. /// The Linux implementation is based on mutex3 from /// https://www.akkadia.org/drepper/futex.pdf -pub const Mutex = struct { +pub const Mutex = switch(builtin.os) { + builtin.Os.linux => MutexLinux, + builtin.Os.windows => MutexWindows, + else => MutexSpinLock, +}; + +const MutexLinux = struct { /// 0: unlocked /// 1: locked, no waiters /// 2: locked, one or more waiters - linux_lock: @typeOf(linux_lock_init), - - /// TODO better implementation than spin lock - spin_lock: @typeOf(spin_lock_init), - - const linux_lock_init = if (builtin.os == builtin.Os.linux) i32(0) else {}; - const spin_lock_init = if (builtin.os != builtin.Os.linux) SpinLock.init() else {}; + lock: i32, pub const Held = struct { mutex: *Mutex, pub fn release(self: Held) void { - if (builtin.os == builtin.Os.linux) { - const c = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release); - if (c != 1) { - _ = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release); - const rc = linux.futex_wake(&self.mutex.linux_lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); - switch (linux.getErrno(rc)) { - 0 => {}, - linux.EINVAL => unreachable, - else => unreachable, - } + const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release); + if (c != 1) { + _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release); + const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); + switch (linux.getErrno(rc)) { + 0 => {}, + linux.EINVAL => unreachable, + else => unreachable, } - } else { - SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.spin_lock }); } } }; pub fn init() Mutex { - return Mutex{ - .linux_lock = linux_lock_init, - .spin_lock = spin_lock_init, + return Mutex { + .lock = 0, }; } + pub fn deinit(self: *Mutex) void {} + pub fn acquire(self: *Mutex) Held { - if (builtin.os == builtin.Os.linux) { - var c = @cmpxchgWeak(i32, &self.linux_lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse - return Held{ .mutex = self }; - if (c != 2) - c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); - while (c != 0) { - const rc = linux.futex_wait(&self.linux_lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null); - switch (linux.getErrno(rc)) { - 0, linux.EINTR, linux.EAGAIN => {}, - linux.EINVAL => unreachable, - else => unreachable, - } - c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); + var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse + return Held{ .mutex = self }; + if (c != 2) + c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); + while (c != 0) { + const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null); + switch (linux.getErrno(rc)) { + 0, linux.EINTR, linux.EAGAIN => {}, + linux.EINVAL => unreachable, + else => unreachable, } - } else { - _ = self.spin_lock.acquire(); + c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); } - return Held{ .mutex = self }; + return Held { .mutex = self }; + } +}; + +const MutexWindows = struct { + lock: ?windows.RTL_CRITICAL_SECTION, + + pub const Held = struct { + mutex: *Mutex, + + pub fn release(self: Held) void { + windows.LeaveCriticalSection(&self.mutex.lock); + } + }; + + fn initOsData(self: *MutexWindows) void { + if (self.lock == null) { + windows.InitializeCriticalSection(&self.lock); + } + } + + pub fn init() Mutex { + return Mutex { + .lock = null, + }; + } + + pub fn deinit(self: *Mutex) void { + windows.DeleteCriticalSection(&self.lock); + self.lock = null; + } + + pub fn acquire(self: *Mutex) Held { + self.initOsData(); + windows.EnterCriticalSection(&self.lock); + return Held { .mutex = self }; + } +}; + +const MutexSpinLock = struct { + /// TODO better implementation than spin lock + lock: SpinLock, + + pub const Held = struct { + mutex: *Mutex, + + pub fn release(self: Held) void { + SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock }); + } + }; + + pub fn init() Mutex { + return Mutex { + .lock = SpinLock.init(), + }; + } + + pub fn deinit(self: *Mutex) void {} + + pub fn acquire(self: *Mutex) Held { + _ = self.spin_lock.acquire(); + return Held { .mutex = self }; } }; @@ -90,6 +145,8 @@ test "std.Mutex" { var a = &fixed_buffer_allocator.allocator; var mutex = Mutex.init(); + defer mutex.deinit(); + var context = Context{ .mutex = &mutex, .data = 0, diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 202b8bffeb..1691b6523f 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -220,3 +220,34 @@ pub const FOREGROUND_BLUE = 1; pub const FOREGROUND_GREEN = 2; pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; + +pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; + +pub const LIST_ENTRY = extern struct { + Flink: *LIST_ENTRY, + Blink: *LIST_ENTRY, +}; + +pub const RTL_CRITICAL_SECTION_DEBUG = extern struct { + Type: WORD, + CreatorBackTraceIndex: WORD, + CriticalSection: *RTL_CRITICAL_SECTION, + ProcessLocksList: LIST_ENTRY, + EntryCount: DWORD, + ContentionCount: DWORD, + Flags: DWORD, + CreatorBackTraceIndexHigh: WORD, + SpareWORD: WORD, +}; + +pub const RTL_CRITICAL_SECTION = extern struct { + DebugInfo: *RTL_CRITICAL_SECTION_DEBUG, + LockCount: i32, + RecursionCount: i32, + OwningThread: HANDLE, + LockSemaphore: HANDLE, + SpinCount: ULONG_PTR, +}; From 99f44219bcc0c8c496a4a5ebd5d8be465cb71c07 Mon Sep 17 00:00:00 2001 From: emekoi Date: Sun, 18 Nov 2018 00:27:47 -0600 Subject: [PATCH 02/11] updated structs --- std/mutex.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/mutex.zig b/std/mutex.zig index 25e6a5bd25..125a763479 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -122,7 +122,7 @@ const MutexSpinLock = struct { pub fn deinit(self: *Mutex) void {} pub fn acquire(self: *Mutex) Held { - _ = self.spin_lock.acquire(); + _ = self.lock.acquire(); return Held { .mutex = self }; } }; From aaae2f5705a2dee3a12ea49c1094c217a73bb897 Mon Sep 17 00:00:00 2001 From: emekoi Date: Sun, 18 Nov 2018 01:15:24 -0600 Subject: [PATCH 03/11] switching from EnterCriticalSection to TryEnterCriticalSection --- std/mutex.zig | 2 +- std/os/windows/kernel32.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 125a763479..c0b5d3b002 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -96,7 +96,7 @@ const MutexWindows = struct { pub fn acquire(self: *Mutex) Held { self.initOsData(); - windows.EnterCriticalSection(&self.lock); + while (windows.TryEnterCriticalSection(&self.lock) == 0) {} return Held { .mutex = self }; } }; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 1691b6523f..141039a155 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -222,7 +222,7 @@ pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn TryEnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) BOOL; pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; From 25d7f5b65475d087ee1f046a364f8a9e6cd60135 Mon Sep 17 00:00:00 2001 From: emekoi Date: Sun, 18 Nov 2018 18:32:50 -0600 Subject: [PATCH 04/11] switching back to EnterCriticalSection --- std/mutex.zig | 13 +++++-------- std/os/windows/kernel32.zig | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index c0b5d3b002..01ff891b80 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -77,12 +77,6 @@ const MutexWindows = struct { } }; - fn initOsData(self: *MutexWindows) void { - if (self.lock == null) { - windows.InitializeCriticalSection(&self.lock); - } - } - pub fn init() Mutex { return Mutex { .lock = null, @@ -95,8 +89,11 @@ const MutexWindows = struct { } pub fn acquire(self: *Mutex) Held { - self.initOsData(); - while (windows.TryEnterCriticalSection(&self.lock) == 0) {} + if (self.lock == null) { + windows.InitializeCriticalSection(&self.lock); + } + + windows.EnterCriticalSection(&self.lock); return Held { .mutex = self }; } }; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 141039a155..1691b6523f 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -222,7 +222,7 @@ pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn TryEnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) BOOL; +pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; From 207fa3849ca61a6b22084d2d173b36f23ec841bd Mon Sep 17 00:00:00 2001 From: emekoi Date: Mon, 19 Nov 2018 13:52:10 -0600 Subject: [PATCH 05/11] moved to InitializeCriticalSection to init --- std/mutex.zig | 174 +++++++++++++++++------------------- std/os/windows/kernel32.zig | 8 +- 2 files changed, 87 insertions(+), 95 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 01ff891b80..9307637253 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -12,116 +12,108 @@ const windows = std.os.windows; /// The Linux implementation is based on mutex3 from /// https://www.akkadia.org/drepper/futex.pdf pub const Mutex = switch(builtin.os) { - builtin.Os.linux => MutexLinux, - builtin.Os.windows => MutexWindows, - else => MutexSpinLock, -}; + builtin.Os.linux => struct { + /// 0: unlocked + /// 1: locked, no waiters + /// 2: locked, one or more waiters + lock: i32, -const MutexLinux = struct { - /// 0: unlocked - /// 1: locked, no waiters - /// 2: locked, one or more waiters - lock: i32, + pub const Held = struct { + mutex: *Mutex, - pub const Held = struct { - mutex: *Mutex, + pub fn release(self: Held) void { + const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release); + if (c != 1) { + _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release); + const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); + switch (linux.getErrno(rc)) { + 0 => {}, + linux.EINVAL => unreachable, + else => unreachable, + } + } + } + }; - pub fn release(self: Held) void { - const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release); - if (c != 1) { - _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release); - const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); + pub fn init() Mutex { + return Mutex { + .lock = 0, + }; + } + + pub fn deinit(self: *Mutex) void {} + + pub fn acquire(self: *Mutex) Held { + var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse + return Held{ .mutex = self }; + if (c != 2) + c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); + while (c != 0) { + const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null); switch (linux.getErrno(rc)) { - 0 => {}, + 0, linux.EINTR, linux.EAGAIN => {}, linux.EINVAL => unreachable, else => unreachable, } + c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); + } + return Held { .mutex = self }; + } + }, + builtin.Os.windows => struct { + lock: ?*windows.RTL_CRITICAL_SECTION, + + pub const Held = struct { + mutex: *Mutex, + + pub fn release(self: Held) void { + windows.LeaveCriticalSection(self.mutex.lock); + } + }; + + pub fn init() Mutex { + var lock: ?*windows.RTL_CRITICAL_SECTION = null; + windows.InitializeCriticalSection(lock); + return Mutex { .lock = lock }; + } + + pub fn deinit(self: *Mutex) void { + if (self.lock != null) { + windows.DeleteCriticalSection(self.lock); + self.lock = null; } } - }; - pub fn init() Mutex { - return Mutex { - .lock = 0, - }; - } + pub fn acquire(self: *Mutex) Held { + windows.EnterCriticalSection(self.lock); + return Held { .mutex = self }; + } + }, + else => struct { + /// TODO better implementation than spin lock + lock: SpinLock, - pub fn deinit(self: *Mutex) void {} + pub const Held = struct { + mutex: *Mutex, - pub fn acquire(self: *Mutex) Held { - var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse - return Held{ .mutex = self }; - if (c != 2) - c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); - while (c != 0) { - const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null); - switch (linux.getErrno(rc)) { - 0, linux.EINTR, linux.EAGAIN => {}, - linux.EINVAL => unreachable, - else => unreachable, + pub fn release(self: Held) void { + SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock }); } - c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); - } - return Held { .mutex = self }; - } -}; - -const MutexWindows = struct { - lock: ?windows.RTL_CRITICAL_SECTION, - - pub const Held = struct { - mutex: *Mutex, - - pub fn release(self: Held) void { - windows.LeaveCriticalSection(&self.mutex.lock); - } - }; - - pub fn init() Mutex { - return Mutex { - .lock = null, }; - } - pub fn deinit(self: *Mutex) void { - windows.DeleteCriticalSection(&self.lock); - self.lock = null; - } - - pub fn acquire(self: *Mutex) Held { - if (self.lock == null) { - windows.InitializeCriticalSection(&self.lock); + pub fn init() Mutex { + return Mutex { + .lock = SpinLock.init(), + }; } - windows.EnterCriticalSection(&self.lock); - return Held { .mutex = self }; - } -}; + pub fn deinit(self: *Mutex) void {} -const MutexSpinLock = struct { - /// TODO better implementation than spin lock - lock: SpinLock, - - pub const Held = struct { - mutex: *Mutex, - - pub fn release(self: Held) void { - SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock }); + pub fn acquire(self: *Mutex) Held { + _ = self.lock.acquire(); + return Held { .mutex = self }; } - }; - - pub fn init() Mutex { - return Mutex { - .lock = SpinLock.init(), - }; - } - - pub fn deinit(self: *Mutex) void {} - - pub fn acquire(self: *Mutex) Held { - _ = self.lock.acquire(); - return Held { .mutex = self }; - } + }, }; const Context = struct { diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 1691b6523f..e34092ad51 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -221,10 +221,10 @@ pub const FOREGROUND_GREEN = 2; pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; -pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; pub const LIST_ENTRY = extern struct { Flink: *LIST_ENTRY, From bb31695fbfbf7c808dc3fd8c3cfc0b654ed15e5e Mon Sep 17 00:00:00 2001 From: emekoi Date: Mon, 26 Nov 2018 21:07:01 -0600 Subject: [PATCH 06/11] fixed mutex on windows --- std/mutex.zig | 54 +++++++++++++++++++++++++++---------- std/os/windows/index.zig | 1 + std/os/windows/kernel32.zig | 24 ++++++++++++----- 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 9307637253..45336f353e 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -61,31 +61,57 @@ pub const Mutex = switch(builtin.os) { } }, builtin.Os.windows => struct { - lock: ?*windows.RTL_CRITICAL_SECTION, + + lock: ?windows.CRITICAL_SECTION, + init_once: windows.PINIT_ONCE, pub const Held = struct { mutex: *Mutex, pub fn release(self: Held) void { - windows.LeaveCriticalSection(self.mutex.lock); + if (self.mutex.lock) |*lock| { + windows.LeaveCriticalSection(lock); + } } }; pub fn init() Mutex { - var lock: ?*windows.RTL_CRITICAL_SECTION = null; - windows.InitializeCriticalSection(lock); - return Mutex { .lock = lock }; + return Mutex { + .lock = null, + .init_once = undefined, + }; + } + + extern fn initCriticalSection(InitOnce: *windows.PINIT_ONCE, Parameter: ?windows.PVOID, Context: ?*windows.PVOID) windows.BOOL { + if (Context) |ctx| { + var mutex = @ptrCast(*?windows.CRITICAL_SECTION, ctx); + if (mutex.* == null) { + var lock: windows.CRITICAL_SECTION = undefined; + windows.InitializeCriticalSection(&lock); + mutex.* = lock; + } + return windows.TRUE; + } + return windows.FALSE; } pub fn deinit(self: *Mutex) void { - if (self.lock != null) { - windows.DeleteCriticalSection(self.lock); - self.lock = null; + if (self.lock) |*lock| { + windows.DeleteCriticalSection(lock); } } pub fn acquire(self: *Mutex) Held { - windows.EnterCriticalSection(self.lock); + if (self.lock) |*lock| { + windows.EnterCriticalSection(lock); + } else { + if (windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, null, @ptrCast(?*windows.PVOID, self)) == windows.TRUE) { + windows.EnterCriticalSection(&self.lock.?); + } else { + @panic("unable to initialize Mutex"); + } + } + return Held { .mutex = self }; } }, @@ -116,7 +142,7 @@ pub const Mutex = switch(builtin.os) { }, }; -const Context = struct { +const TestContext = struct { mutex: *Mutex, data: i128, @@ -136,7 +162,7 @@ test "std.Mutex" { var mutex = Mutex.init(); defer mutex.deinit(); - var context = Context{ + var context = TestContext{ .mutex = &mutex, .data = 0, }; @@ -149,12 +175,12 @@ test "std.Mutex" { for (threads) |t| t.wait(); - std.debug.assertOrPanic(context.data == thread_count * Context.incr_count); + std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); } -fn worker(ctx: *Context) void { +fn worker(ctx: *TestContext) void { var i: usize = 0; - while (i != Context.incr_count) : (i += 1) { + while (i != TestContext.incr_count) : (i += 1) { const held = ctx.mutex.acquire(); defer held.release(); diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 3d6ee67113..3f19905835 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -49,6 +49,7 @@ pub const UNICODE = false; pub const WCHAR = u16; pub const WORD = u16; pub const LARGE_INTEGER = i64; +pub const LONG = c_long; pub const TRUE = 1; pub const FALSE = 0; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index e34092ad51..07d6cd80a2 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -221,10 +221,10 @@ pub const FOREGROUND_GREEN = 2; pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; -pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; pub const LIST_ENTRY = extern struct { Flink: *LIST_ENTRY, @@ -245,9 +245,21 @@ pub const RTL_CRITICAL_SECTION_DEBUG = extern struct { pub const RTL_CRITICAL_SECTION = extern struct { DebugInfo: *RTL_CRITICAL_SECTION_DEBUG, - LockCount: i32, - RecursionCount: i32, + LockCount: LONG, + RecursionCount: LONG, OwningThread: HANDLE, LockSemaphore: HANDLE, SpinCount: ULONG_PTR, }; + +pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; + +pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *PINIT_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?*LPVOID) BOOL; + +pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *PINIT_ONCE, Parameter: ?PVOID, Context: ?*PVOID) BOOL; + +pub const RTL_RUN_ONCE = extern struct { + Ptr: PVOID, +}; + +pub const PINIT_ONCE = RTL_RUN_ONCE; From 93851270526e71407b4225a23daa66214a4010f9 Mon Sep 17 00:00:00 2001 From: emekoi Date: Tue, 27 Nov 2018 01:39:37 -0600 Subject: [PATCH 07/11] changed pointer types --- std/mutex.zig | 7 ++++--- std/os/windows/kernel32.zig | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 45336f353e..3469b9a118 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -82,9 +82,9 @@ pub const Mutex = switch(builtin.os) { }; } - extern fn initCriticalSection(InitOnce: *windows.PINIT_ONCE, Parameter: ?windows.PVOID, Context: ?*windows.PVOID) windows.BOOL { + extern fn initCriticalSection(InitOnce: *windows.PINIT_ONCE, Parameter: ?windows.PVOID, Context: ?windows.PVOID) windows.BOOL { if (Context) |ctx| { - var mutex = @ptrCast(*?windows.CRITICAL_SECTION, ctx); + var mutex = @ptrCast(*?windows.CRITICAL_SECTION, @alignCast(8, ctx)); if (mutex.* == null) { var lock: windows.CRITICAL_SECTION = undefined; windows.InitializeCriticalSection(&lock); @@ -105,7 +105,8 @@ pub const Mutex = switch(builtin.os) { if (self.lock) |*lock| { windows.EnterCriticalSection(lock); } else { - if (windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, null, @ptrCast(?*windows.PVOID, self)) == windows.TRUE) { + var mutex = @ptrCast(?windows.PVOID, self); + if (windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, null, mutex) == windows.TRUE) { windows.EnterCriticalSection(&self.lock.?); } else { @panic("unable to initialize Mutex"); diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 07d6cd80a2..56e2163fb6 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -254,9 +254,9 @@ pub const RTL_CRITICAL_SECTION = extern struct { pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; -pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *PINIT_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?*LPVOID) BOOL; +pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *PINIT_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?LPVOID) BOOL; -pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *PINIT_ONCE, Parameter: ?PVOID, Context: ?*PVOID) BOOL; +pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *PINIT_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; pub const RTL_RUN_ONCE = extern struct { Ptr: PVOID, From 35d93d22db6f1cdf20011b0db84586107513b462 Mon Sep 17 00:00:00 2001 From: emekoi Date: Tue, 27 Nov 2018 16:20:09 +0000 Subject: [PATCH 08/11] removed nullables --- std/mutex.zig | 55 ++++++++++++++++--------------------- std/os/windows/kernel32.zig | 12 ++++---- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 3469b9a118..30b68511ea 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -62,57 +62,50 @@ pub const Mutex = switch(builtin.os) { }, builtin.Os.windows => struct { - lock: ?windows.CRITICAL_SECTION, - init_once: windows.PINIT_ONCE, + lock: windows.CRITICAL_SECTION, + init_once: windows.RTL_RUN_ONCE, pub const Held = struct { mutex: *Mutex, pub fn release(self: Held) void { - if (self.mutex.lock) |*lock| { - windows.LeaveCriticalSection(lock); - } + windows.LeaveCriticalSection(&self.mutex.lock); } }; pub fn init() Mutex { - return Mutex { - .lock = null, + return Mutex { + .lock = undefined, .init_once = undefined, }; } - extern fn initCriticalSection(InitOnce: *windows.PINIT_ONCE, Parameter: ?windows.PVOID, Context: ?windows.PVOID) windows.BOOL { - if (Context) |ctx| { - var mutex = @ptrCast(*?windows.CRITICAL_SECTION, @alignCast(8, ctx)); - if (mutex.* == null) { - var lock: windows.CRITICAL_SECTION = undefined; - windows.InitializeCriticalSection(&lock); - mutex.* = lock; - } - return windows.TRUE; - } - return windows.FALSE; + extern fn initCriticalSection( + InitOnce: *windows.RTL_RUN_ONCE, + Parameter: ?windows.PVOID, + Context: ?windows.PVOID + ) windows.BOOL { + var lock = @ptrCast( + *windows.CRITICAL_SECTION, + @alignCast(@alignOf(*windows.CRITICAL_SECTION), ctx.?) + ); + windows.InitializeCriticalSection(lock); + return windows.TRUE; } pub fn deinit(self: *Mutex) void { - if (self.lock) |*lock| { - windows.DeleteCriticalSection(lock); - } + windows.DeleteCriticalSection(&self.lock); } pub fn acquire(self: *Mutex) Held { - if (self.lock) |*lock| { - windows.EnterCriticalSection(lock); - } else { - var mutex = @ptrCast(?windows.PVOID, self); - if (windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, null, mutex) == windows.TRUE) { - windows.EnterCriticalSection(&self.lock.?); - } else { - @panic("unable to initialize Mutex"); - } + if (windows.InitOnceExecuteOnce( + &self.init_once, + initCriticalSection, + null, @ptrCast(?windows.PVOID, self) + ) == windows.FALSE) { + unreachable; } - + windows.EnterCriticalSection(&self.lock); return Held { .mutex = self }; } }, diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 56e2163fb6..a0b23acf5d 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -222,9 +222,9 @@ pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; pub const LIST_ENTRY = extern struct { Flink: *LIST_ENTRY, @@ -254,12 +254,10 @@ pub const RTL_CRITICAL_SECTION = extern struct { pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; -pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *PINIT_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?LPVOID) BOOL; +pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *RTL_RUN_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?LPVOID) BOOL; -pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *PINIT_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; +pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *RTL_RUN_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; pub const RTL_RUN_ONCE = extern struct { Ptr: PVOID, }; - -pub const PINIT_ONCE = RTL_RUN_ONCE; From 51fff9fa8212f514f76ef69c214e570d4ef98655 Mon Sep 17 00:00:00 2001 From: emekoi Date: Tue, 27 Nov 2018 17:43:59 +0000 Subject: [PATCH 09/11] fixed initializer and typos --- std/mutex.zig | 4 ++-- std/os/windows/kernel32.zig | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 30b68511ea..2885a99ed7 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -76,7 +76,7 @@ pub const Mutex = switch(builtin.os) { pub fn init() Mutex { return Mutex { .lock = undefined, - .init_once = undefined, + .init_once = windows.INIT_ONCE_STATIC_INIT, }; } @@ -87,7 +87,7 @@ pub const Mutex = switch(builtin.os) { ) windows.BOOL { var lock = @ptrCast( *windows.CRITICAL_SECTION, - @alignCast(@alignOf(*windows.CRITICAL_SECTION), ctx.?) + @alignCast(@alignOf(*windows.CRITICAL_SECTION), Context.?) ); windows.InitializeCriticalSection(lock); return windows.TRUE; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index a0b23acf5d..8fd351c125 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -261,3 +261,7 @@ pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *RTL_RUN_ONCE, Parameter: ?PVOID, pub const RTL_RUN_ONCE = extern struct { Ptr: PVOID, }; + +pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE { + .Ptr = null, +}; \ No newline at end of file From 3ff9ab332cf909da12361bf6ea495b2025ca8833 Mon Sep 17 00:00:00 2001 From: emekoi Date: Tue, 27 Nov 2018 17:54:09 +0000 Subject: [PATCH 10/11] fixed type signature --- std/os/windows/kernel32.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 8fd351c125..7a6126133a 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -259,7 +259,7 @@ pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *RTL_RUN_ONCE, pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *RTL_RUN_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; pub const RTL_RUN_ONCE = extern struct { - Ptr: PVOID, + Ptr: ?PVOID, }; pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE { From bfc1772d8ead9e8fcde0a315411ddcd040cc3d28 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Feb 2019 12:22:21 -0500 Subject: [PATCH 11/11] fixups --- CMakeLists.txt | 31 ++++---- std/fmt/index.zig | 2 - std/index.zig | 2 + std/mutex.zig | 61 +++------------- std/os/windows/kernel32.zig | 14 ++-- std/statically_initialized_mutex.zig | 105 +++++++++++++++++++++++++++ 6 files changed, 142 insertions(+), 73 deletions(-) create mode 100644 std/statically_initialized_mutex.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 3841519f29..4dd6a1dcfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -454,10 +454,10 @@ set(ZIG_STD_FILES "crypto/hmac.zig" "crypto/index.zig" "crypto/md5.zig" + "crypto/poly1305.zig" "crypto/sha1.zig" "crypto/sha2.zig" "crypto/sha3.zig" - "crypto/poly1305.zig" "crypto/x25519.zig" "cstr.zig" "debug/failing_allocator.zig" @@ -566,9 +566,9 @@ set(ZIG_STD_FILES "math/tan.zig" "math/tanh.zig" "math/trunc.zig" + "mem.zig" "meta/index.zig" "meta/trait.zig" - "mem.zig" "mutex.zig" "net.zig" "os/child_process.zig" @@ -576,16 +576,16 @@ set(ZIG_STD_FILES "os/darwin/errno.zig" "os/epoch.zig" "os/file.zig" + "os/freebsd/errno.zig" + "os/freebsd/index.zig" "os/get_app_data_dir.zig" "os/get_user_id.zig" "os/index.zig" + "os/linux/arm64.zig" "os/linux/errno.zig" "os/linux/index.zig" "os/linux/vdso.zig" "os/linux/x86_64.zig" - "os/linux/arm64.zig" - "os/freebsd/errno.zig" - "os/freebsd/index.zig" "os/path.zig" "os/time.zig" "os/uefi.zig" @@ -612,6 +612,16 @@ set(ZIG_STD_FILES "special/compiler_rt/comparetf2.zig" "special/compiler_rt/divti3.zig" "special/compiler_rt/extendXfYf2.zig" + "special/compiler_rt/fixdfdi.zig" + "special/compiler_rt/fixdfsi.zig" + "special/compiler_rt/fixdfti.zig" + "special/compiler_rt/fixint.zig" + "special/compiler_rt/fixsfdi.zig" + "special/compiler_rt/fixsfsi.zig" + "special/compiler_rt/fixsfti.zig" + "special/compiler_rt/fixtfdi.zig" + "special/compiler_rt/fixtfsi.zig" + "special/compiler_rt/fixtfti.zig" "special/compiler_rt/fixuint.zig" "special/compiler_rt/fixunsdfdi.zig" "special/compiler_rt/fixunsdfsi.zig" @@ -622,16 +632,6 @@ set(ZIG_STD_FILES "special/compiler_rt/fixunstfdi.zig" "special/compiler_rt/fixunstfsi.zig" "special/compiler_rt/fixunstfti.zig" - "special/compiler_rt/fixint.zig" - "special/compiler_rt/fixdfdi.zig" - "special/compiler_rt/fixdfsi.zig" - "special/compiler_rt/fixdfti.zig" - "special/compiler_rt/fixsfdi.zig" - "special/compiler_rt/fixsfsi.zig" - "special/compiler_rt/fixsfti.zig" - "special/compiler_rt/fixtfdi.zig" - "special/compiler_rt/fixtfsi.zig" - "special/compiler_rt/fixtfti.zig" "special/compiler_rt/floattidf.zig" "special/compiler_rt/floattisf.zig" "special/compiler_rt/floattitf.zig" @@ -656,6 +656,7 @@ set(ZIG_STD_FILES "special/panic.zig" "special/test_runner.zig" "spinlock.zig" + "statically_initialized_mutex.zig" "unicode.zig" "zig/ast.zig" "zig/index.zig" diff --git a/std/fmt/index.zig b/std/fmt/index.zig index b010072273..6097a12c23 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -982,13 +982,11 @@ test "fmt.format" { context = BufPrintContext{ .remaining = buf1[0..] }; try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite); res = buf1[0 .. buf1.len - context.remaining.len]; - debug.warn("{}\n", res); assert(mem.eql(u8, res, "a")); context = BufPrintContext{ .remaining = buf1[0..] }; try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite); res = buf1[0 .. buf1.len - context.remaining.len]; - debug.warn("{}\n", res); assert(mem.eql(u8, res, "1100")); } { diff --git a/std/index.zig b/std/index.zig index 33eec14b0e..80d1e46bb6 100644 --- a/std/index.zig +++ b/std/index.zig @@ -9,6 +9,7 @@ pub const DynLib = @import("dynamic_library.zig").DynLib; pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const Mutex = @import("mutex.zig").Mutex; +pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; pub const SpinLock = @import("spinlock.zig").SpinLock; @@ -55,6 +56,7 @@ test "std" { _ = @import("hash_map.zig"); _ = @import("linked_list.zig"); _ = @import("mutex.zig"); + _ = @import("statically_initialized_mutex.zig"); _ = @import("segmented_list.zig"); _ = @import("spinlock.zig"); diff --git a/std/mutex.zig b/std/mutex.zig index 2885a99ed7..723581cbef 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -9,6 +9,9 @@ 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 must be initialized at runtime, and then deinitialized when no +/// longer needed, to free resources. +/// If you need static initialization, use std.StaticallyInitializedMutex. /// The Linux implementation is based on mutex3 from /// https://www.akkadia.org/drepper/futex.pdf pub const Mutex = switch(builtin.os) { @@ -60,57 +63,15 @@ pub const Mutex = switch(builtin.os) { return Held { .mutex = self }; } }, - builtin.Os.windows => struct { - - lock: windows.CRITICAL_SECTION, - init_once: windows.RTL_RUN_ONCE, - - pub const Held = struct { - mutex: *Mutex, - - pub fn release(self: Held) void { - windows.LeaveCriticalSection(&self.mutex.lock); - } - }; - - pub fn init() Mutex { - return Mutex { - .lock = undefined, - .init_once = windows.INIT_ONCE_STATIC_INIT, - }; - } - - extern fn initCriticalSection( - InitOnce: *windows.RTL_RUN_ONCE, - Parameter: ?windows.PVOID, - Context: ?windows.PVOID - ) windows.BOOL { - var lock = @ptrCast( - *windows.CRITICAL_SECTION, - @alignCast(@alignOf(*windows.CRITICAL_SECTION), Context.?) - ); - windows.InitializeCriticalSection(lock); - return windows.TRUE; - } - - pub fn deinit(self: *Mutex) void { - windows.DeleteCriticalSection(&self.lock); - } - - pub fn acquire(self: *Mutex) Held { - if (windows.InitOnceExecuteOnce( - &self.init_once, - initCriticalSection, - null, @ptrCast(?windows.PVOID, self) - ) == windows.FALSE) { - unreachable; - } - windows.EnterCriticalSection(&self.lock); - return Held { .mutex = self }; - } - }, + // TODO once https://github.com/ziglang/zig/issues/287 (copy elision) is solved, we can make a + // better implementation of this. The problem is we need the init() function to have access to + // the address of the CRITICAL_SECTION, and then have it not move. + builtin.Os.windows => std.StaticallyInitializedMutex, else => struct { - /// TODO better implementation than spin lock + /// TODO better implementation than spin lock. + /// When changing this, one must also change the corresponding + /// std.StaticallyInitializedMutex code, since it aliases this type, + /// under the assumption that it works both statically and at runtime. lock: SpinLock, pub const Held = struct { diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 7a6126133a..66b9552c5f 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -253,15 +253,17 @@ pub const RTL_CRITICAL_SECTION = extern struct { }; pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; +pub const INIT_ONCE = RTL_RUN_ONCE; +pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; -pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *RTL_RUN_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?LPVOID) BOOL; +pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL; -pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *RTL_RUN_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; +pub const INIT_ONCE_FN = extern fn(InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; pub const RTL_RUN_ONCE = extern struct { - Ptr: ?PVOID, + Ptr: ?*c_void, }; -pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE { - .Ptr = null, -}; \ No newline at end of file +pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE { + .Ptr = null, +}; diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig new file mode 100644 index 0000000000..dd875eeaf9 --- /dev/null +++ b/std/statically_initialized_mutex.zig @@ -0,0 +1,105 @@ +const std = @import("index.zig"); +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; +const AtomicRmwOp = builtin.AtomicRmwOp; +const assert = std.debug.assert; +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. +pub const StaticallyInitializedMutex = switch(builtin.os) { + 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 { + windows.LeaveCriticalSection(&self.mutex.lock); + } + }; + + pub fn init() StaticallyInitializedMutex { + return StaticallyInitializedMutex { + .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)); + windows.InitializeCriticalSection(lock); + 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 { + assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); + windows.DeleteCriticalSection(&self.lock); + } + + pub fn acquire(self: *StaticallyInitializedMutex) Held { + assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); + windows.EnterCriticalSection(&self.lock); + return Held { .mutex = self }; + } + }, + 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; + + + var context = TestContext{ + .data = 0, + }; + + const thread_count = 10; + var threads: [thread_count]*std.os.Thread = undefined; + for (threads) |*t| { + t.* = try std.os.spawnThread(&context, TestContext.worker); + } + for (threads) |t| + t.wait(); + + std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); +}