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;...@@ -5,74 +5,138 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const SpinLock = std.SpinLock;6const SpinLock = std.SpinLock;
7const linux = std.os.linux;7const linux = std.os.linux;
8const windows = std.os.windows;
89
9/// Lock may be held only once. If the same thread10/// Lock may be held only once. If the same thread
10/// tries to acquire the same mutex twice, it deadlocks.11/// tries to acquire the same mutex twice, it deadlocks.
11/// The Linux implementation is based on mutex3 from12/// The Linux implementation is based on mutex3 from
12/// https://www.akkadia.org/drepper/futex.pdf13/// https://www.akkadia.org/drepper/futex.pdf
13pub const Mutex = struct {14pub const Mutex = switch(builtin.os) {
14 /// 0: unlocked15 builtin.Os.linux => struct {
15 /// 1: locked, no waiters16 /// 0: unlocked
16 /// 2: locked, one or more waiters17 /// 1: locked, no waiters
17 linux_lock: @typeOf(linux_lock_init),18 /// 2: locked, one or more waiters
1819 lock: i32,
19 /// TODO better implementation than spin lock20
20 spin_lock: @typeOf(spin_lock_init),21 pub const Held = struct {
2122 mutex: *Mutex,
22 const linux_lock_init = if (builtin.os == builtin.Os.linux) i32(0) else {};23
23 const spin_lock_init = if (builtin.os != builtin.Os.linux) SpinLock.init() else {};24 pub fn release(self: Held) void {
2425 const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release);
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);
31 if (c != 1) {26 if (c != 1) {
32 _ = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release);27 _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release);
33 const rc = linux.futex_wake(&self.mutex.linux_lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);28 const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
34 switch (linux.getErrno(rc)) {29 switch (linux.getErrno(rc)) {
35 0 => {},30 0 => {},
36 linux.EINVAL => unreachable,31 linux.EINVAL => unreachable,
37 else => unreachable,32 else => unreachable,
38 }33 }
39 }34 }
40 } else {
41 SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.spin_lock });
42 }35 }
36 };
37
38 pub fn init() Mutex {
39 return Mutex {
40 .lock = 0,
41 };
43 }42 }
44 };
4543
46 pub fn init() Mutex {44 pub fn deinit(self: *Mutex) void {}
47 return Mutex{
48 .linux_lock = linux_lock_init,
49 .spin_lock = spin_lock_init,
50 };
51 }
5245
53 pub fn acquire(self: *Mutex) Held {46 pub fn acquire(self: *Mutex) Held {
54 if (builtin.os == builtin.Os.linux) {47 var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse
55 var c = @cmpxchgWeak(i32, &self.linux_lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse
56 return Held{ .mutex = self };48 return Held{ .mutex = self };
57 if (c != 2)49 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);
59 while (c != 0) {51 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);
61 switch (linux.getErrno(rc)) {53 switch (linux.getErrno(rc)) {
62 0, linux.EINTR, linux.EAGAIN => {},54 0, linux.EINTR, linux.EAGAIN => {},
63 linux.EINVAL => unreachable,55 linux.EINVAL => unreachable,
64 else => unreachable,56 else => unreachable,
65 }57 }
66 c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);58 c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
67 }59 }
68 } else {60 return Held { .mutex = self };
69 _ = self.spin_lock.acquire();
70 }61 }
71 return Held{ .mutex = self };62 },
72 }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 },
73};137};
74138
75const Context = struct {139const TestContext = struct {
76 mutex: *Mutex,140 mutex: *Mutex,
77 data: i128,141 data: i128,
78142
...@@ -90,7 +154,9 @@ test "std.Mutex" {...@@ -90,7 +154,9 @@ test "std.Mutex" {
90 var a = &fixed_buffer_allocator.allocator;154 var a = &fixed_buffer_allocator.allocator;
91155
92 var mutex = Mutex.init();156 var mutex = Mutex.init();
93 var context = Context{157 defer mutex.deinit();
158
159 var context = TestContext{
94 .mutex = &mutex,160 .mutex = &mutex,
95 .data = 0,161 .data = 0,
96 };162 };
...@@ -103,12 +169,12 @@ test "std.Mutex" {...@@ -103,12 +169,12 @@ test "std.Mutex" {
103 for (threads) |t|169 for (threads) |t|
104 t.wait();170 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);
107}173}
108174
109fn worker(ctx: *Context) void {175fn worker(ctx: *TestContext) void {
110 var i: usize = 0;176 var i: usize = 0;
111 while (i != Context.incr_count) : (i += 1) {177 while (i != TestContext.incr_count) : (i += 1) {
112 const held = ctx.mutex.acquire();178 const held = ctx.mutex.acquire();
113 defer held.release();179 defer held.release();
114180
std/os/windows/index.zig+1
...@@ -49,6 +49,7 @@ pub const UNICODE = false;...@@ -49,6 +49,7 @@ pub const UNICODE = false;
49pub const WCHAR = u16;49pub const WCHAR = u16;
50pub const WORD = u16;50pub const WORD = u16;
51pub const LARGE_INTEGER = i64;51pub const LARGE_INTEGER = i64;
52pub const LONG = c_long;
5253
53pub const TRUE = 1;54pub const TRUE = 1;
54pub const FALSE = 0;55pub const FALSE = 0;
std/os/windows/kernel32.zig+45
...@@ -220,3 +220,48 @@ pub const FOREGROUND_BLUE = 1;...@@ -220,3 +220,48 @@ pub const FOREGROUND_BLUE = 1;
220pub const FOREGROUND_GREEN = 2;220pub const FOREGROUND_GREEN = 2;
221pub const FOREGROUND_RED = 4;221pub const FOREGROUND_RED = 4;
222pub const FOREGROUND_INTENSITY = 8;222pub 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