authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-01 10:21:16-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-01 10:21:16-05:00
log8d3eb25e92c7a7597711cdb9a7268059d795fc15
tree63dafb0ba61588c9cf6a2bf19d70aeec77bead7a
parent7a4ed10981cf7ac5bed93b2c9bad280ad31c50cf
parent3ff9ab332cf909da12361bf6ea495b2025ca8833

Merge branch 'windows-mutex' of https://github.com/emekoi/zig into emekoi-windows-mutex


3 files changed, 156 insertions(+), 44 deletions(-)

std/mutex.zig+110-44
......@@ -5,74 +5,138 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
55const assert = std.debug.assert;
66const SpinLock = std.SpinLock;
77const linux = std.os.linux;
8const windows = std.os.windows;
89
910/// Lock may be held only once. If the same thread
1011/// tries to acquire the same mutex twice, it deadlocks.
1112/// The Linux implementation is based on mutex3 from
1213/// https://www.akkadia.org/drepper/futex.pdf
13pub const Mutex = struct {
14 /// 0: unlocked
15 /// 1: locked, no waiters
16 /// 2: locked, one or more waiters
17 linux_lock: @typeOf(linux_lock_init),
18
19 /// TODO better implementation than spin lock
20 spin_lock: @typeOf(spin_lock_init),
21
22 const linux_lock_init = if (builtin.os == builtin.Os.linux) i32(0) else {};
23 const spin_lock_init = if (builtin.os != builtin.Os.linux) SpinLock.init() else {};
24
25 pub const Held = struct {
26 mutex: *Mutex,
27
28 pub fn release(self: Held) void {
29 if (builtin.os == builtin.Os.linux) {
30 const c = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release);
14pub const Mutex = switch(builtin.os) {
15 builtin.Os.linux => struct {
16 /// 0: unlocked
17 /// 1: locked, no waiters
18 /// 2: locked, one or more waiters
19 lock: i32,
20
21 pub const Held = struct {
22 mutex: *Mutex,
23
24 pub fn release(self: Held) void {
25 const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release);
3126 if (c != 1) {
32 _ = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release);
33 const rc = linux.futex_wake(&self.mutex.linux_lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
27 _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release);
28 const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
3429 switch (linux.getErrno(rc)) {
3530 0 => {},
3631 linux.EINVAL => unreachable,
3732 else => unreachable,
3833 }
3934 }
40 } else {
41 SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.spin_lock });
4235 }
36 };
37
38 pub fn init() Mutex {
39 return Mutex {
40 .lock = 0,
41 };
4342 }
44 };
4543
46 pub fn init() Mutex {
47 return Mutex{
48 .linux_lock = linux_lock_init,
49 .spin_lock = spin_lock_init,
50 };
51 }
44 pub fn deinit(self: *Mutex) void {}
5245
53 pub fn acquire(self: *Mutex) Held {
54 if (builtin.os == builtin.Os.linux) {
55 var c = @cmpxchgWeak(i32, &self.linux_lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse
46 pub fn acquire(self: *Mutex) Held {
47 var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse
5648 return Held{ .mutex = self };
5749 if (c != 2)
58 c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
50 c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
5951 while (c != 0) {
60 const rc = linux.futex_wait(&self.linux_lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null);
52 const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null);
6153 switch (linux.getErrno(rc)) {
6254 0, linux.EINTR, linux.EAGAIN => {},
6355 linux.EINVAL => unreachable,
6456 else => unreachable,
6557 }
66 c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
58 c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
6759 }
68 } else {
69 _ = self.spin_lock.acquire();
60 return Held { .mutex = self };
7061 }
71 return Held{ .mutex = self };
72 }
62 },
63 builtin.Os.windows => struct {
64
65 lock: windows.CRITICAL_SECTION,
66 init_once: windows.RTL_RUN_ONCE,
67
68 pub const Held = struct {
69 mutex: *Mutex,
70
71 pub fn release(self: Held) void {
72 windows.LeaveCriticalSection(&self.mutex.lock);
73 }
74 };
75
76 pub fn init() Mutex {
77 return Mutex {
78 .lock = undefined,
79 .init_once = windows.INIT_ONCE_STATIC_INIT,
80 };
81 }
82
83 extern fn initCriticalSection(
84 InitOnce: *windows.RTL_RUN_ONCE,
85 Parameter: ?windows.PVOID,
86 Context: ?windows.PVOID
87 ) windows.BOOL {
88 var lock = @ptrCast(
89 *windows.CRITICAL_SECTION,
90 @alignCast(@alignOf(*windows.CRITICAL_SECTION), Context.?)
91 );
92 windows.InitializeCriticalSection(lock);
93 return windows.TRUE;
94 }
95
96 pub fn deinit(self: *Mutex) void {
97 windows.DeleteCriticalSection(&self.lock);
98 }
99
100 pub fn acquire(self: *Mutex) Held {
101 if (windows.InitOnceExecuteOnce(
102 &self.init_once,
103 initCriticalSection,
104 null, @ptrCast(?windows.PVOID, self)
105 ) == windows.FALSE) {
106 unreachable;
107 }
108 windows.EnterCriticalSection(&self.lock);
109 return Held { .mutex = self };
110 }
111 },
112 else => struct {
113 /// TODO better implementation than spin lock
114 lock: SpinLock,
115
116 pub const Held = struct {
117 mutex: *Mutex,
118
119 pub fn release(self: Held) void {
120 SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock });
121 }
122 };
123
124 pub fn init() Mutex {
125 return Mutex {
126 .lock = SpinLock.init(),
127 };
128 }
129
130 pub fn deinit(self: *Mutex) void {}
131
132 pub fn acquire(self: *Mutex) Held {
133 _ = self.lock.acquire();
134 return Held { .mutex = self };
135 }
136 },
73137};
74138
75const Context = struct {
139const TestContext = struct {
76140 mutex: *Mutex,
77141 data: i128,
78142
......@@ -90,7 +154,9 @@ test "std.Mutex" {
90154 var a = &fixed_buffer_allocator.allocator;
91155
92156 var mutex = Mutex.init();
93 var context = Context{
157 defer mutex.deinit();
158
159 var context = TestContext{
94160 .mutex = &mutex,
95161 .data = 0,
96162 };
......@@ -103,12 +169,12 @@ test "std.Mutex" {
103169 for (threads) |t|
104170 t.wait();
105171
106 std.debug.assertOrPanic(context.data == thread_count * Context.incr_count);
172 std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count);
107173}
108174
109fn worker(ctx: *Context) void {
175fn worker(ctx: *TestContext) void {
110176 var i: usize = 0;
111 while (i != Context.incr_count) : (i += 1) {
177 while (i != TestContext.incr_count) : (i += 1) {
112178 const held = ctx.mutex.acquire();
113179 defer held.release();
114180
std/os/windows/index.zig+1
......@@ -49,6 +49,7 @@ pub const UNICODE = false;
4949pub const WCHAR = u16;
5050pub const WORD = u16;
5151pub const LARGE_INTEGER = i64;
52pub const LONG = c_long;
5253
5354pub const TRUE = 1;
5455pub const FALSE = 0;
std/os/windows/kernel32.zig+45
......@@ -220,3 +220,48 @@ pub const FOREGROUND_BLUE = 1;
220220pub const FOREGROUND_GREEN = 2;
221221pub const FOREGROUND_RED = 4;
222222pub const FOREGROUND_INTENSITY = 8;
223
224pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
225pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
226pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
227pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
228
229pub const LIST_ENTRY = extern struct {
230 Flink: *LIST_ENTRY,
231 Blink: *LIST_ENTRY,
232};
233
234pub const RTL_CRITICAL_SECTION_DEBUG = extern struct {
235 Type: WORD,
236 CreatorBackTraceIndex: WORD,
237 CriticalSection: *RTL_CRITICAL_SECTION,
238 ProcessLocksList: LIST_ENTRY,
239 EntryCount: DWORD,
240 ContentionCount: DWORD,
241 Flags: DWORD,
242 CreatorBackTraceIndexHigh: WORD,
243 SpareWORD: WORD,
244};
245
246pub const RTL_CRITICAL_SECTION = extern struct {
247 DebugInfo: *RTL_CRITICAL_SECTION_DEBUG,
248 LockCount: LONG,
249 RecursionCount: LONG,
250 OwningThread: HANDLE,
251 LockSemaphore: HANDLE,
252 SpinCount: ULONG_PTR,
253};
254
255pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION;
256
257pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *RTL_RUN_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?LPVOID) BOOL;
258
259pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *RTL_RUN_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL;
260
261pub const RTL_RUN_ONCE = extern struct {
262 Ptr: ?PVOID,
263};
264
265pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE {
266 .Ptr = null,
267};
\ No newline at end of file