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;
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 {
14pub const Mutex = switch(builtin.os) {
15 builtin.Os.linux => MutexLinux,
16 builtin.Os.windows => MutexWindows,
17 else => MutexSpinLock,
18};
19
20const MutexLinux = struct {
1421 /// 0: unlocked
1522 /// 1: locked, no waiters
1623 /// 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 lock: i32,
2425
2526 pub const Held = struct {
2627 mutex: *Mutex,
2728
2829 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) {
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);
34 switch (linux.getErrno(rc)) {
35 0 => {},
36 linux.EINVAL => unreachable,
37 else => unreachable,
38 }
30 const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release);
31 if (c != 1) {
32 _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release);
33 const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
34 switch (linux.getErrno(rc)) {
35 0 => {},
36 linux.EINVAL => unreachable,
37 else => unreachable,
3938 }
40 } else {
41 SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.spin_lock });
4239 }
4340 }
4441 };
4542
4643 pub fn init() Mutex {
47 return Mutex{
48 .linux_lock = linux_lock_init,
49 .spin_lock = spin_lock_init,
44 return Mutex {
45 .lock = 0,
5046 };
5147 }
5248
49 pub fn deinit(self: *Mutex) void {}
50
5351 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
56 return Held{ .mutex = self };
57 if (c != 2)
58 c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
59 while (c != 0) {
60 const rc = linux.futex_wait(&self.linux_lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null);
61 switch (linux.getErrno(rc)) {
62 0, linux.EINTR, linux.EAGAIN => {},
63 linux.EINVAL => unreachable,
64 else => unreachable,
65 }
66 c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
52 var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse
53 return Held{ .mutex = self };
54 if (c != 2)
55 c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
56 while (c != 0) {
57 const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null);
58 switch (linux.getErrno(rc)) {
59 0, linux.EINTR, linux.EAGAIN => {},
60 linux.EINVAL => unreachable,
61 else => unreachable,
6762 }
68 } else {
69 _ = self.spin_lock.acquire();
63 c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire);
7064 }
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 };
72127 }
73128};
74129
......@@ -90,6 +145,8 @@ test "std.Mutex" {
90145 var a = &fixed_buffer_allocator.allocator;
91146
92147 var mutex = Mutex.init();
148 defer mutex.deinit();
149
93150 var context = Context{
94151 .mutex = &mutex,
95152 .data = 0,
std/os/windows/kernel32.zig+31
......@@ -220,3 +220,34 @@ 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: *?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};