| ... | ... | @@ -76,107 +76,39 @@ const WindowsParker = struct { |
| 76 | 76 | pub fn deinit(self: *WindowsParker) void {} |
| 77 | 77 | |
| 78 | 78 | pub fn unpark(self: *WindowsParker, ptr: *const u32) void { |
| 79 | | switch (Backend.get().*) { |
| 80 | | .WaitAddress => |*backend| backend.unpark(ptr, &self.waiters), |
| 81 | | .KeyedEvent => |*backend| backend.unpark(ptr, &self.waiters), |
| 79 | const handle = getEventHandlePtr().*; |
| 80 | const key = @ptrCast(*const c_void, ptr); |
| 81 | var waiting = @atomicLoad(u32, &self.waiters, .Monotonic); |
| 82 | while (waiting != 0) { |
| 83 | waiting = @cmpxchgWeak(u32, &self.waiters, waiting, waiting - 1, .Acquire, .Monotonic) orelse { |
| 84 | const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null); |
| 85 | assert(rc == 0); |
| 86 | return; |
| 87 | }; |
| 82 | 88 | } |
| 83 | 89 | } |
| 84 | 90 | |
| 85 | 91 | pub fn park(self: *WindowsParker, ptr: *const u32, expected: u32) void { |
| 86 | | switch (Backend.get().*) { |
| 87 | | .WaitAddress => |*backend| backend.park(ptr, expected, &self.waiters), |
| 88 | | .KeyedEvent => |*backend| backend.park(ptr, expected, &self.waiters), |
| 92 | const handle = getEventHandlePtr().*; |
| 93 | const key = @ptrCast(*const c_void, ptr); |
| 94 | while (@atomicLoad(u32, ptr, .Acquire) == expected) { |
| 95 | _ = @atomicRmw(u32, &self.waiters, .Add, 1, .Release); |
| 96 | const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null); |
| 97 | assert(rc == 0); |
| 89 | 98 | } |
| 90 | 99 | } |
| 91 | 100 | |
| 92 | | const Backend = union(enum) { |
| 93 | | WaitAddress: WaitAddress, |
| 94 | | KeyedEvent: KeyedEvent, |
| 95 | | |
| 96 | | var backend = std.lazyInit(Backend); |
| 97 | | |
| 98 | | fn get() *const Backend { |
| 99 | | return backend.get() orelse { |
| 100 | | // Statically linking to the KeyedEvent functions should mean its supported. |
| 101 | | // TODO: Maybe add a CreateSemaphore backend for systems older than Windows XP ? |
| 102 | | backend.data = WaitAddress.init() |
| 103 | | orelse KeyedEvent.init() |
| 104 | | orelse unreachable; |
| 105 | | backend.resolve(); |
| 106 | | return &backend.data; |
| 107 | | }; |
| 108 | | } |
| 109 | | |
| 110 | | const WaitAddress = struct { |
| 111 | | WakeByAddressSingle: stdcallcc fn(Address: *const c_void) void, |
| 112 | | WaitOnAddress: stdcallcc fn ( |
| 113 | | Address: *const c_void, |
| 114 | | CompareAddress: *const c_void, |
| 115 | | AddressSize: windows.SIZE_T, |
| 116 | | dwMilliseconds: windows.DWORD, |
| 117 | | ) windows.BOOL, |
| 118 | | |
| 119 | | fn init() ?Backend { |
| 120 | | const dll_name = c"api-ms-win-core-synch-l1-2-0"; |
| 121 | | const dll = windows.kernel32.GetModuleHandleA(dll_name) |
| 122 | | orelse windows.kernel32.LoadLibraryA(dll_name) |
| 123 | | orelse return null; |
| 124 | | |
| 125 | | var self: WaitAddress = undefined; |
| 126 | | const WaitOnAddress = windows.kernel32.GetProcAddress(dll, c"WaitOnAddress") orelse return null; |
| 127 | | self.WaitOnAddress = @intToPtr(@typeOf(self.WaitOnAddress), @ptrToInt(WaitOnAddress)); |
| 128 | | const WakeByAddressSingle = windows.kernel32.GetProcAddress(dll, c"WakeByAddressSingle") orelse return null; |
| 129 | | self.WakeByAddressSingle = @intToPtr(@typeOf(self.WakeByAddressSingle), @ptrToInt(WakeByAddressSingle)); |
| 130 | | return Backend{ .WaitAddress = self }; |
| 131 | | } |
| 132 | | |
| 133 | | fn unpark(self: WaitAddress, ptr: *const u32, waiters: *u32) void { |
| 134 | | const addr = @ptrCast(*const c_void, ptr); |
| 135 | | self.WakeByAddressSingle(addr); |
| 136 | | } |
| 101 | var event_handle = std.lazyInit(windows.HANDLE); |
| 137 | 102 | |
| 138 | | fn park(self: WaitAddress, ptr: *const u32, expected: u32, waiters: *u32) void { |
| 139 | | var compare = expected; |
| 140 | | const addr = @ptrCast(*const c_void, ptr); |
| 141 | | const cmp = @ptrCast(*const c_void, &compare); |
| 142 | | while (@atomicLoad(u32, ptr, .Acquire) == expected) |
| 143 | | _ = self.WaitOnAddress(addr, cmp, @sizeOf(u32), windows.INFINITE); |
| 144 | | } |
| 103 | fn getEventHandlePtr() *const windows.HANDLE { |
| 104 | return event_handle.get() orelse { |
| 105 | const access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE; |
| 106 | if (windows.ntdll.NtCreateKeyedEvent(&event_handle.data, access_mask, null, 0) != 0) |
| 107 | @panic("Failed to setup an NT Keyed Event handle for the process"); |
| 108 | event_handle.resolve(); |
| 109 | return &event_handle.data; |
| 145 | 110 | }; |
| 146 | | |
| 147 | | const KeyedEvent = struct { |
| 148 | | handle: windows.HANDLE, |
| 149 | | |
| 150 | | fn init() ?Backend { |
| 151 | | var self: KeyedEvent = undefined; |
| 152 | | const access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE; |
| 153 | | if (windows.ntdll.NtCreateKeyedEvent(&self.handle, access_mask, null, 0) != 0) |
| 154 | | return null; |
| 155 | | return Backend{ .KeyedEvent = self }; |
| 156 | | } |
| 157 | | |
| 158 | | fn unpark(self: KeyedEvent, ptr: *const u32, waiters: *u32) void { |
| 159 | | const key = @ptrCast(*const c_void, ptr); |
| 160 | | var waiting = @atomicLoad(u32, waiters, .Acquire); |
| 161 | | while (waiting != 0) { |
| 162 | | waiting = @cmpxchgWeak(u32, waiters, waiting, waiting - 1, .Acquire, .Monotonic) orelse { |
| 163 | | const rc = windows.ntdll.NtReleaseKeyedEvent(self.handle, key, windows.FALSE, null); |
| 164 | | assert(rc == 0); |
| 165 | | return; |
| 166 | | }; |
| 167 | | } |
| 168 | | } |
| 169 | | |
| 170 | | fn park(self: KeyedEvent, ptr: *const u32, expected: u32, waiters: *u32) void { |
| 171 | | const key = @ptrCast(*const c_void, ptr); |
| 172 | | while (@atomicLoad(u32, ptr, .Acquire) == expected) { |
| 173 | | _ = @atomicRmw(u32, waiters, .Add, 1, .Release); |
| 174 | | const rc = windows.ntdll.NtWaitForKeyedEvent(self.handle, key, windows.FALSE, null); |
| 175 | | assert(rc == 0); |
| 176 | | } |
| 177 | | } |
| 178 | | }; |
| 179 | | }; |
| 111 | } |
| 180 | 112 | }; |
| 181 | 113 | |
| 182 | 114 | const PosixParker = struct { |