| ... | @@ -36,6 +36,14 @@ const Thread = struct { | ... | @@ -36,6 +36,14 @@ const Thread = struct { |
| 36 | kq_fd: posix.fd_t, | 36 | kq_fd: posix.fd_t, |
| 37 | idle_search_index: u32, | 37 | idle_search_index: u32, |
| 38 | steal_ready_search_index: u32, | 38 | steal_ready_search_index: u32, |
| | 39 | /// For ensuring multiple fibers waiting on the same file descriptor and |
| | 40 | /// filter use the same kevent. |
| | 41 | wait_queues: std.AutoArrayHashMapUnmanaged(WaitQueueKey, *Fiber), |
| | 42 | |
| | 43 | const WaitQueueKey = struct { |
| | 44 | ident: usize, |
| | 45 | filter: i32, |
| | 46 | }; |
| 39 | | 47 | |
| 40 | const canceling: ?*Thread = @ptrFromInt(@alignOf(Thread)); | 48 | const canceling: ?*Thread = @ptrFromInt(@alignOf(Thread)); |
| 41 | | 49 | |
| ... | @@ -54,6 +62,13 @@ const Thread = struct { | ... | @@ -54,6 +62,13 @@ const Thread = struct { |
| 54 | reserved: u32, | 62 | reserved: u32, |
| 55 | active: u32, | 63 | active: u32, |
| 56 | }; | 64 | }; |
| | 65 | |
| | 66 | fn deinit(thread: *Thread, gpa: Allocator) void { |
| | 67 | posix.close(thread.kq_fd); |
| | 68 | assert(thread.wait_queues.count() == 0); |
| | 69 | thread.wait_queues.deinit(gpa); |
| | 70 | thread.* = undefined; |
| | 71 | } |
| 57 | }; | 72 | }; |
| 58 | | 73 | |
| 59 | const Fiber = struct { | 74 | const Fiber = struct { |
| ... | @@ -138,8 +153,14 @@ fn recycle(k: *Kqueue, fiber: *Fiber) void { | ... | @@ -138,8 +153,14 @@ fn recycle(k: *Kqueue, fiber: *Fiber) void { |
| 138 | k.gpa.free(fiber.allocatedSlice()); | 153 | k.gpa.free(fiber.allocatedSlice()); |
| 139 | } | 154 | } |
| 140 | | 155 | |
| 141 | pub fn init(k: *Kqueue, gpa: Allocator) !void { | 156 | pub const InitOptions = struct { |
| 142 | const threads_size = @max(std.Thread.getCpuCount() catch 1, 1) * @sizeOf(Thread); | 157 | n_threads: ?usize = null, |
| | 158 | }; |
| | 159 | |
| | 160 | pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void { |
| | 161 | assert(options.n_threads != 0); |
| | 162 | const n_threads = @max(1, options.n_threads orelse std.Thread.getCpuCount() catch 1); |
| | 163 | const threads_size = n_threads * @sizeOf(Thread); |
| 143 | const idle_stack_end_offset = std.mem.alignForward(usize, threads_size + idle_stack_size, std.heap.page_size_max); | 164 | const idle_stack_end_offset = std.mem.alignForward(usize, threads_size + idle_stack_size, std.heap.page_size_max); |
| 144 | const allocated_slice = try gpa.alignedAlloc(u8, .of(Thread), idle_stack_end_offset); | 165 | const allocated_slice = try gpa.alignedAlloc(u8, .of(Thread), idle_stack_end_offset); |
| 145 | errdefer gpa.free(allocated_slice); | 166 | errdefer gpa.free(allocated_slice); |
| ... | @@ -186,6 +207,7 @@ pub fn init(k: *Kqueue, gpa: Allocator) !void { | ... | @@ -186,6 +207,7 @@ pub fn init(k: *Kqueue, gpa: Allocator) !void { |
| 186 | .kq_fd = try posix.kqueue(), | 207 | .kq_fd = try posix.kqueue(), |
| 187 | .idle_search_index = 1, | 208 | .idle_search_index = 1, |
| 188 | .steal_ready_search_index = 1, | 209 | .steal_ready_search_index = 1, |
| | 210 | .wait_queues = .empty, |
| 189 | }; | 211 | }; |
| 190 | errdefer std.posix.close(main_thread.kq_fd); | 212 | errdefer std.posix.close(main_thread.kq_fd); |
| 191 | std.log.debug("created main idle {*}", .{&main_thread.idle_context}); | 213 | std.log.debug("created main idle {*}", .{&main_thread.idle_context}); |
| ... | @@ -199,10 +221,13 @@ pub fn deinit(k: *Kqueue) void { | ... | @@ -199,10 +221,13 @@ pub fn deinit(k: *Kqueue) void { |
| 199 | assert(ready_fiber == null or ready_fiber == Fiber.finished); // pending async | 221 | assert(ready_fiber == null or ready_fiber == Fiber.finished); // pending async |
| 200 | } | 222 | } |
| 201 | k.yield(null, .exit); | 223 | k.yield(null, .exit); |
| | 224 | const main_thread = &k.threads.allocated[0]; |
| | 225 | const gpa = k.gpa; |
| | 226 | main_thread.deinit(gpa); |
| 202 | const allocated_ptr: [*]align(@alignOf(Thread)) u8 = @ptrCast(@alignCast(k.threads.allocated.ptr)); | 227 | const allocated_ptr: [*]align(@alignOf(Thread)) u8 = @ptrCast(@alignCast(k.threads.allocated.ptr)); |
| 203 | const idle_stack_end_offset = std.mem.alignForward(usize, k.threads.allocated.len * @sizeOf(Thread) + idle_stack_size, std.heap.page_size_max); | 228 | const idle_stack_end_offset = std.mem.alignForward(usize, k.threads.allocated.len * @sizeOf(Thread) + idle_stack_size, std.heap.page_size_max); |
| 204 | for (k.threads.allocated[1..active_threads]) |*thread| thread.thread.join(); | 229 | for (k.threads.allocated[1..active_threads]) |*thread| thread.thread.join(); |
| 205 | k.gpa.free(allocated_ptr[0..idle_stack_end_offset]); | 230 | gpa.free(allocated_ptr[0..idle_stack_end_offset]); |
| 206 | k.* = undefined; | 231 | k.* = undefined; |
| 207 | } | 232 | } |
| 208 | | 233 | |
| ... | @@ -317,6 +342,7 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void { | ... | @@ -317,6 +342,7 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void { |
| 317 | }, | 342 | }, |
| 318 | .idle_search_index = 0, | 343 | .idle_search_index = 0, |
| 319 | .steal_ready_search_index = 0, | 344 | .steal_ready_search_index = 0, |
| | 345 | .wait_queues = .empty, |
| 320 | }; | 346 | }; |
| 321 | new_thread.thread = std.Thread.spawn(.{ | 347 | new_thread.thread = std.Thread.spawn(.{ |
| 322 | .stack_size = idle_stack_size, | 348 | .stack_size = idle_stack_size, |
| ... | @@ -355,6 +381,7 @@ fn threadEntry(k: *Kqueue, index: u32) void { | ... | @@ -355,6 +381,7 @@ fn threadEntry(k: *Kqueue, index: u32) void { |
| 355 | Thread.self = thread; | 381 | Thread.self = thread; |
| 356 | std.log.debug("created thread idle {*}", .{&thread.idle_context}); | 382 | std.log.debug("created thread idle {*}", .{&thread.idle_context}); |
| 357 | k.idle(thread); | 383 | k.idle(thread); |
| | 384 | thread.deinit(k.gpa); |
| 358 | } | 385 | } |
| 359 | | 386 | |
| 360 | const Completion = struct { | 387 | const Completion = struct { |