| ... | @@ -4,6 +4,7 @@ const assert = std.debug.assert; | ... | @@ -4,6 +4,7 @@ const assert = std.debug.assert; |
| 4 | const event = this; | 4 | const event = this; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | const posix = std.os.posix; | 6 | const posix = std.os.posix; |
| | 7 | const windows = std.os.windows; |
| 7 | const AtomicRmwOp = builtin.AtomicRmwOp; | 8 | const AtomicRmwOp = builtin.AtomicRmwOp; |
| 8 | const AtomicOrder = builtin.AtomicOrder; | 9 | const AtomicOrder = builtin.AtomicOrder; |
| 9 | | 10 | |
| ... | @@ -113,10 +114,10 @@ pub const Loop = struct { | ... | @@ -113,10 +114,10 @@ pub const Loop = struct { |
| 113 | allocator: *mem.Allocator, | 114 | allocator: *mem.Allocator, |
| 114 | next_tick_queue: std.atomic.QueueMpsc(promise), | 115 | next_tick_queue: std.atomic.QueueMpsc(promise), |
| 115 | os_data: OsData, | 116 | os_data: OsData, |
| | 117 | final_resume_node: ResumeNode, |
| 116 | dispatch_lock: u8, // TODO make this a bool | 118 | dispatch_lock: u8, // TODO make this a bool |
| 117 | pending_event_count: usize, | 119 | pending_event_count: usize, |
| 118 | extra_threads: []*std.os.Thread, | 120 | extra_threads: []*std.os.Thread, |
| 119 | final_resume_node: ResumeNode, | | |
| 120 | | 121 | |
| 121 | // pre-allocated eventfds. all permanently active. | 122 | // pre-allocated eventfds. all permanently active. |
| 122 | // this is how we send promises to be resumed on other threads. | 123 | // this is how we send promises to be resumed on other threads. |
| ... | @@ -144,6 +145,7 @@ pub const Loop = struct { | ... | @@ -144,6 +145,7 @@ pub const Loop = struct { |
| 144 | }, | 145 | }, |
| 145 | builtin.Os.windows => struct { | 146 | builtin.Os.windows => struct { |
| 146 | base: ResumeNode, | 147 | base: ResumeNode, |
| | 148 | completion_key: usize, |
| 147 | }, | 149 | }, |
| 148 | else => @compileError("unsupported OS"), | 150 | else => @compileError("unsupported OS"), |
| 149 | }; | 151 | }; |
| ... | @@ -181,12 +183,12 @@ pub const Loop = struct { | ... | @@ -181,12 +183,12 @@ pub const Loop = struct { |
| 181 | .next_tick_queue = std.atomic.QueueMpsc(promise).init(), | 183 | .next_tick_queue = std.atomic.QueueMpsc(promise).init(), |
| 182 | .dispatch_lock = 1, // start locked so threads go directly into epoll wait | 184 | .dispatch_lock = 1, // start locked so threads go directly into epoll wait |
| 183 | .extra_threads = undefined, | 185 | .extra_threads = undefined, |
| | 186 | .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), |
| | 187 | .eventfd_resume_nodes = undefined, |
| 184 | .final_resume_node = ResumeNode{ | 188 | .final_resume_node = ResumeNode{ |
| 185 | .id = ResumeNode.Id.Stop, | 189 | .id = ResumeNode.Id.Stop, |
| 186 | .handle = undefined, | 190 | .handle = undefined, |
| 187 | }, | 191 | }, |
| 188 | .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(), | | |
| 189 | .eventfd_resume_nodes = undefined, | | |
| 190 | }; | 192 | }; |
| 191 | const extra_thread_count = thread_count - 1; | 193 | const extra_thread_count = thread_count - 1; |
| 192 | self.eventfd_resume_nodes = try self.allocator.alloc( | 194 | self.eventfd_resume_nodes = try self.allocator.alloc( |
| ... | @@ -209,7 +211,8 @@ pub const Loop = struct { | ... | @@ -209,7 +211,8 @@ pub const Loop = struct { |
| 209 | } | 211 | } |
| 210 | | 212 | |
| 211 | const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || | 213 | const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || |
| 212 | std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError; | 214 | std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError || |
| | 215 | std.os.WindowsCreateIoCompletionPortError; |
| 213 | | 216 | |
| 214 | const wakeup_bytes = []u8{0x1} ** 8; | 217 | const wakeup_bytes = []u8{0x1} ** 8; |
| 215 | | 218 | |
| ... | @@ -335,6 +338,51 @@ pub const Loop = struct { | ... | @@ -335,6 +338,51 @@ pub const Loop = struct { |
| 335 | self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); | 338 | self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); |
| 336 | } | 339 | } |
| 337 | }, | 340 | }, |
| | 341 | builtin.Os.windows => { |
| | 342 | self.os_data.extra_thread_count = extra_thread_count; |
| | 343 | |
| | 344 | self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( |
| | 345 | windows.INVALID_HANDLE_VALUE, |
| | 346 | null, |
| | 347 | undefined, |
| | 348 | undefined, |
| | 349 | ); |
| | 350 | errdefer std.os.close(self.os_data.io_port); |
| | 351 | |
| | 352 | for (self.eventfd_resume_nodes) |*eventfd_node, i| { |
| | 353 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| | 354 | .data = ResumeNode.EventFd{ |
| | 355 | .base = ResumeNode{ |
| | 356 | .id = ResumeNode.Id.EventFd, |
| | 357 | .handle = undefined, |
| | 358 | }, |
| | 359 | // this one is for sending events |
| | 360 | .completion_key = @ptrToInt(&eventfd_node.data.base), |
| | 361 | }, |
| | 362 | .next = undefined, |
| | 363 | }; |
| | 364 | self.available_eventfd_resume_nodes.push(eventfd_node); |
| | 365 | } |
| | 366 | |
| | 367 | var extra_thread_index: usize = 0; |
| | 368 | errdefer { |
| | 369 | var i: usize = 0; |
| | 370 | while (i < extra_thread_index) : (i += 1) { |
| | 371 | while (true) { |
| | 372 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); |
| | 373 | std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; |
| | 374 | break; |
| | 375 | } |
| | 376 | } |
| | 377 | while (extra_thread_index != 0) { |
| | 378 | extra_thread_index -= 1; |
| | 379 | self.extra_threads[extra_thread_index].wait(); |
| | 380 | } |
| | 381 | } |
| | 382 | while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { |
| | 383 | self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); |
| | 384 | } |
| | 385 | }, |
| 338 | else => {}, | 386 | else => {}, |
| 339 | } | 387 | } |
| 340 | } | 388 | } |
| ... | @@ -349,6 +397,10 @@ pub const Loop = struct { | ... | @@ -349,6 +397,10 @@ pub const Loop = struct { |
| 349 | }, | 397 | }, |
| 350 | builtin.Os.macosx => { | 398 | builtin.Os.macosx => { |
| 351 | self.allocator.free(self.os_data.kevents); | 399 | self.allocator.free(self.os_data.kevents); |
| | 400 | std.os.close(self.os_data.kqfd); |
| | 401 | }, |
| | 402 | builtin.Os.windows => { |
| | 403 | std.os.close(self.os_data.io_port); |
| 352 | }, | 404 | }, |
| 353 | else => {}, | 405 | else => {}, |
| 354 | } | 406 | } |
| ... | @@ -434,7 +486,7 @@ pub const Loop = struct { | ... | @@ -434,7 +486,7 @@ pub const Loop = struct { |
| 434 | builtin.Os.macosx => { | 486 | builtin.Os.macosx => { |
| 435 | const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); | 487 | const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); |
| 436 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; | 488 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; |
| 437 | _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch |_| { | 489 | _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { |
| 438 | // fine, we didn't need it anyway | 490 | // fine, we didn't need it anyway |
| 439 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | 491 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); |
| 440 | self.available_eventfd_resume_nodes.push(resume_stack_node); | 492 | self.available_eventfd_resume_nodes.push(resume_stack_node); |
| ... | @@ -446,7 +498,21 @@ pub const Loop = struct { | ... | @@ -446,7 +498,21 @@ pub const Loop = struct { |
| 446 | builtin.Os.linux => { | 498 | builtin.Os.linux => { |
| 447 | // the pending count is already accounted for | 499 | // the pending count is already accounted for |
| 448 | const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET; | 500 | const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET; |
| 449 | self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch |_| { | 501 | self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch { |
| | 502 | // fine, we didn't need it anyway |
| | 503 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); |
| | 504 | self.available_eventfd_resume_nodes.push(resume_stack_node); |
| | 505 | resume handle; |
| | 506 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); |
| | 507 | continue :start_over; |
| | 508 | }; |
| | 509 | }, |
| | 510 | builtin.Os.windows => { |
| | 511 | // this value is never dereferenced but we need it to be non-null so that |
| | 512 | // the consumer code can decide whether to read the completion key. |
| | 513 | // it has to do this for normal I/O, so we match that behavior here. |
| | 514 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); |
| | 515 | std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, eventfd_node.completion_key, overlapped) catch { |
| 450 | // fine, we didn't need it anyway | 516 | // fine, we didn't need it anyway |
| 451 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | 517 | _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); |
| 452 | self.available_eventfd_resume_nodes.push(resume_stack_node); | 518 | self.available_eventfd_resume_nodes.push(resume_stack_node); |
| ... | @@ -482,6 +548,17 @@ pub const Loop = struct { | ... | @@ -482,6 +548,17 @@ pub const Loop = struct { |
| 482 | _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; | 548 | _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; |
| 483 | return; | 549 | return; |
| 484 | }, | 550 | }, |
| | 551 | builtin.Os.windows => { |
| | 552 | var i: usize = 0; |
| | 553 | while (i < self.os_data.extra_thread_count) : (i += 1) { |
| | 554 | while (true) { |
| | 555 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); |
| | 556 | std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; |
| | 557 | break; |
| | 558 | } |
| | 559 | } |
| | 560 | return; |
| | 561 | }, |
| 485 | else => @compileError("unsupported OS"), | 562 | else => @compileError("unsupported OS"), |
| 486 | } | 563 | } |
| 487 | } | 564 | } |
| ... | @@ -536,6 +613,35 @@ pub const Loop = struct { | ... | @@ -536,6 +613,35 @@ pub const Loop = struct { |
| 536 | } | 613 | } |
| 537 | } | 614 | } |
| 538 | }, | 615 | }, |
| | 616 | builtin.Os.windows => { |
| | 617 | var completion_key: usize = undefined; |
| | 618 | while (true) { |
| | 619 | var nbytes: windows.DWORD = undefined; |
| | 620 | var overlapped: ?*windows.OVERLAPPED = undefined; |
| | 621 | switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, |
| | 622 | &overlapped, windows.INFINITE)) { |
| | 623 | std.os.WindowsWaitResult.Aborted => return, |
| | 624 | std.os.WindowsWaitResult.Normal => {}, |
| | 625 | } |
| | 626 | if (overlapped != null) break; |
| | 627 | } |
| | 628 | const resume_node = @intToPtr(*ResumeNode, completion_key); |
| | 629 | const handle = resume_node.handle; |
| | 630 | const resume_node_id = resume_node.id; |
| | 631 | switch (resume_node_id) { |
| | 632 | ResumeNode.Id.Basic => {}, |
| | 633 | ResumeNode.Id.Stop => return, |
| | 634 | ResumeNode.Id.EventFd => { |
| | 635 | const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); |
| | 636 | const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); |
| | 637 | self.available_eventfd_resume_nodes.push(stack_node); |
| | 638 | }, |
| | 639 | } |
| | 640 | resume handle; |
| | 641 | if (resume_node_id == ResumeNode.Id.EventFd) { |
| | 642 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); |
| | 643 | } |
| | 644 | }, |
| 539 | else => @compileError("unsupported OS"), | 645 | else => @compileError("unsupported OS"), |
| 540 | } | 646 | } |
| 541 | } | 647 | } |
| ... | @@ -548,6 +654,10 @@ pub const Loop = struct { | ... | @@ -548,6 +654,10 @@ pub const Loop = struct { |
| 548 | final_eventfd_event: std.os.linux.epoll_event, | 654 | final_eventfd_event: std.os.linux.epoll_event, |
| 549 | }, | 655 | }, |
| 550 | builtin.Os.macosx => MacOsData, | 656 | builtin.Os.macosx => MacOsData, |
| | 657 | builtin.Os.windows => struct { |
| | 658 | io_port: windows.HANDLE, |
| | 659 | extra_thread_count: usize, |
| | 660 | }, |
| 551 | else => struct {}, | 661 | else => struct {}, |
| 552 | }; | 662 | }; |
| 553 | | 663 | |