| ... | ... | @@ -36,6 +36,14 @@ const Thread = struct { |
| 36 | 36 | kq_fd: posix.fd_t, |
| 37 | 37 | idle_search_index: u32, |
| 38 | 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 | 48 | const canceling: ?*Thread = @ptrFromInt(@alignOf(Thread)); |
| 41 | 49 | |
| ... | ... | @@ -54,6 +62,13 @@ const Thread = struct { |
| 54 | 62 | reserved: u32, |
| 55 | 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 | 74 | const Fiber = struct { |
| ... | ... | @@ -138,8 +153,14 @@ fn recycle(k: *Kqueue, fiber: *Fiber) void { |
| 138 | 153 | k.gpa.free(fiber.allocatedSlice()); |
| 139 | 154 | } |
| 140 | 155 | |
| 141 | | pub fn init(k: *Kqueue, gpa: Allocator) !void { |
| 142 | | const threads_size = @max(std.Thread.getCpuCount() catch 1, 1) * @sizeOf(Thread); |
| 156 | pub const InitOptions = struct { |
| 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 | 164 | const idle_stack_end_offset = std.mem.alignForward(usize, threads_size + idle_stack_size, std.heap.page_size_max); |
| 144 | 165 | const allocated_slice = try gpa.alignedAlloc(u8, .of(Thread), idle_stack_end_offset); |
| 145 | 166 | errdefer gpa.free(allocated_slice); |
| ... | ... | @@ -186,6 +207,7 @@ pub fn init(k: *Kqueue, gpa: Allocator) !void { |
| 186 | 207 | .kq_fd = try posix.kqueue(), |
| 187 | 208 | .idle_search_index = 1, |
| 188 | 209 | .steal_ready_search_index = 1, |
| 210 | .wait_queues = .empty, |
| 189 | 211 | }; |
| 190 | 212 | errdefer std.posix.close(main_thread.kq_fd); |
| 191 | 213 | std.log.debug("created main idle {*}", .{&main_thread.idle_context}); |
| ... | ... | @@ -199,10 +221,13 @@ pub fn deinit(k: *Kqueue) void { |
| 199 | 221 | assert(ready_fiber == null or ready_fiber == Fiber.finished); // pending async |
| 200 | 222 | } |
| 201 | 223 | k.yield(null, .exit); |
| 224 | const main_thread = &k.threads.allocated[0]; |
| 225 | const gpa = k.gpa; |
| 226 | main_thread.deinit(gpa); |
| 202 | 227 | const allocated_ptr: [*]align(@alignOf(Thread)) u8 = @ptrCast(@alignCast(k.threads.allocated.ptr)); |
| 203 | 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 | 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 | 231 | k.* = undefined; |
| 207 | 232 | } |
| 208 | 233 | |
| ... | ... | @@ -317,6 +342,7 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void { |
| 317 | 342 | }, |
| 318 | 343 | .idle_search_index = 0, |
| 319 | 344 | .steal_ready_search_index = 0, |
| 345 | .wait_queues = .empty, |
| 320 | 346 | }; |
| 321 | 347 | new_thread.thread = std.Thread.spawn(.{ |
| 322 | 348 | .stack_size = idle_stack_size, |
| ... | ... | @@ -355,6 +381,7 @@ fn threadEntry(k: *Kqueue, index: u32) void { |
| 355 | 381 | Thread.self = thread; |
| 356 | 382 | std.log.debug("created thread idle {*}", .{&thread.idle_context}); |
| 357 | 383 | k.idle(thread); |
| 384 | thread.deinit(k.gpa); |
| 358 | 385 | } |
| 359 | 386 | |
| 360 | 387 | const Completion = struct { |