authorgravatar for emekankurumeh@outlook.comemekoi <emekankurumeh@outlook.com> 2018-11-18 00:07:37-06:00
committergravatar for emekankurumeh@outlook.comemekoi <emekankurumeh@outlook.com> 2019-01-11 09:56:32-06:00
logb61bce254cc5c7075e2fb722e7deb0085eebb65e
tree360a2885fb5bac66aa50139378a511a7deb809f5
parent5864d92b4049e6d60a21a3f22220464808dd0518

added mutex for windows


2 files changed, 127 insertions(+), 39 deletions(-)

std/mutex.zig+96-39
...@@ -5,70 +5,125 @@ const AtomicRmwOp = builtin.AtomicRmwOp;...@@ -5,70 +5,125 @@ 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) {
15 builtin.Os.linux => MutexLinux,
16 builtin.Os.windows => MutexWindows,
17 else => MutexSpinLock,
18};
19
20const MutexLinux = struct {
14 /// 0: unlocked21 /// 0: unlocked
15 /// 1: locked, no waiters22 /// 1: locked, no waiters
16 /// 2: locked, one or more waiters23 /// 2: locked, one or more waiters
17 linux_lock: @typeOf(linux_lock_init),24 lock: i32,
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 {};
2425
25 pub const Held = struct {26 pub const Held = struct {
26 mutex: *Mutex,27 mutex: *Mutex,
2728
28 pub fn release(self: Held) void {29 pub fn release(self: Held) void {
29 if (builtin.os == builtin.Os.linux) {30 const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release);
30 const c = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release);31 if (c != 1) {
31 if (c != 1) {32 _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release);
32 _ = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release);33 const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
33 const rc = linux.futex_wake(&self.mutex.linux_lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);34 switch (linux.getErrno(rc)) {
34 switch (linux.getErrno(rc)) {35 0 => {},
35 0 => {},36 linux.EINVAL => unreachable,
36 linux.EINVAL => unreachable,37 else => unreachable,
37 else => unreachable,
38 }
39 }38 }
40 } else {
41 SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.spin_lock });
42 }39 }
43 }40 }
44 };41 };
4542
46 pub fn init() Mutex {43 pub fn init() Mutex {
47 return Mutex{44 return Mutex {
48 .linux_lock = linux_lock_init,45 .lock = 0,
49 .spin_lock = spin_lock_init,
50 };46 };
51 }47 }
5248
49 pub fn deinit(self: *Mutex) void {}
50
53 pub fn acquire(self: *Mutex) Held {51 pub fn acquire(self: *Mutex) Held {
54 if (builtin.os == builtin.Os.linux) {52 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) orelse53 return Held{ .mutex = self };
56 return Held{ .mutex = self };54 if (c != 2)
57 if (c != 2)55 c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
58 c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);56 while (c != 0) {
59 while (c != 0) {57 const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null);
60 const rc = linux.futex_wait(&self.linux_lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null);58 switch (linux.getErrno(rc)) {
61 switch (linux.getErrno(rc)) {59 0, linux.EINTR, linux.EAGAIN => {},
62 0, linux.EINTR, linux.EAGAIN => {},60 linux.EINVAL => unreachable,
63 linux.EINVAL => unreachable,61 else => unreachable,
64 else => unreachable,
65 }
66 c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
67 }62 }
68 } else {63 c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
69 _ = self.spin_lock.acquire();
70 }64 }
71 return Held{ .mutex = self };65 return Held { .mutex = self };
66 }
67};
68
69const MutexWindows = struct {
70 lock: ?windows.RTL_CRITICAL_SECTION,
71
72 pub const Held = struct {
73 mutex: *Mutex,
74
75 pub fn release(self: Held) void {
76 windows.LeaveCriticalSection(&self.mutex.lock);
77 }
78 };
79
80 fn initOsData(self: *MutexWindows) void {
81 if (self.lock == null) {
82 windows.InitializeCriticalSection(&self.lock);
83 }
84 }
85
86 pub fn init() Mutex {
87 return Mutex {
88 .lock = null,
89 };
90 }
91
92 pub fn deinit(self: *Mutex) void {
93 windows.DeleteCriticalSection(&self.lock);
94 self.lock = null;
95 }
96
97 pub fn acquire(self: *Mutex) Held {
98 self.initOsData();
99 windows.EnterCriticalSection(&self.lock);
100 return Held { .mutex = self };
101 }
102};
103
104const MutexSpinLock = struct {
105 /// TODO better implementation than spin lock
106 lock: SpinLock,
107
108 pub const Held = struct {
109 mutex: *Mutex,
110
111 pub fn release(self: Held) void {
112 SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock });
113 }
114 };
115
116 pub fn init() Mutex {
117 return Mutex {
118 .lock = SpinLock.init(),
119 };
120 }
121
122 pub fn deinit(self: *Mutex) void {}
123
124 pub fn acquire(self: *Mutex) Held {
125 _ = self.spin_lock.acquire();
126 return Held { .mutex = self };
72 }127 }
73};128};
74129
...@@ -90,6 +145,8 @@ test "std.Mutex" {...@@ -90,6 +145,8 @@ test "std.Mutex" {
90 var a = &fixed_buffer_allocator.allocator;145 var a = &fixed_buffer_allocator.allocator;
91146
92 var mutex = Mutex.init();147 var mutex = Mutex.init();
148 defer mutex.deinit();
149
93 var context = Context{150 var context = Context{
94 .mutex = &mutex,151 .mutex = &mutex,
95 .data = 0,152 .data = 0,
std/os/windows/kernel32.zig+31
...@@ -220,3 +220,34 @@ pub const FOREGROUND_BLUE = 1;...@@ -220,3 +220,34 @@ 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: *?RTL_CRITICAL_SECTION) void;
225pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void;
226pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void;
227pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *?RTL_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: i32,
249 RecursionCount: i32,
250 OwningThread: HANDLE,
251 LockSemaphore: HANDLE,
252 SpinCount: ULONG_PTR,
253};