| ... | ... | @@ -10,12 +10,9 @@ const IoUring = std.os.linux.IoUring; |
| 10 | 10 | |
| 11 | 11 | /// Must be a thread-safe allocator. |
| 12 | 12 | gpa: Allocator, |
| 13 | mutex: std.Thread.Mutex, |
| 13 | 14 | main_fiber_buffer: [@sizeOf(Fiber) + Fiber.max_result_size]u8 align(@alignOf(Fiber)), |
| 14 | 15 | threads: Thread.List, |
| 15 | | detached: struct { |
| 16 | | mutex: std.Io.Mutex, |
| 17 | | list: std.DoublyLinkedList, |
| 18 | | }, |
| 19 | 16 | |
| 20 | 17 | /// Empirically saw >128KB being used by the self-hosted backend to panic. |
| 21 | 18 | const idle_stack_size = 256 * 1024; |
| ... | ... | @@ -142,7 +139,6 @@ pub fn io(el: *EventLoop) Io { |
| 142 | 139 | .async = async, |
| 143 | 140 | .concurrent = concurrent, |
| 144 | 141 | .await = await, |
| 145 | | .asyncDetached = asyncDetached, |
| 146 | 142 | .select = select, |
| 147 | 143 | .cancel = cancel, |
| 148 | 144 | .cancelRequested = cancelRequested, |
| ... | ... | @@ -172,16 +168,13 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { |
| 172 | 168 | errdefer gpa.free(allocated_slice); |
| 173 | 169 | el.* = .{ |
| 174 | 170 | .gpa = gpa, |
| 171 | .mutex = .{}, |
| 175 | 172 | .main_fiber_buffer = undefined, |
| 176 | 173 | .threads = .{ |
| 177 | 174 | .allocated = @ptrCast(allocated_slice[0..threads_size]), |
| 178 | 175 | .reserved = 1, |
| 179 | 176 | .active = 1, |
| 180 | 177 | }, |
| 181 | | .detached = .{ |
| 182 | | .mutex = .init, |
| 183 | | .list = .{}, |
| 184 | | }, |
| 185 | 178 | }; |
| 186 | 179 | const main_fiber: *Fiber = @ptrCast(&el.main_fiber_buffer); |
| 187 | 180 | main_fiber.* = .{ |
| ... | ... | @@ -223,22 +216,6 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { |
| 223 | 216 | } |
| 224 | 217 | |
| 225 | 218 | pub fn deinit(el: *EventLoop) void { |
| 226 | | while (true) cancel(el, detached_future: { |
| 227 | | el.detached.mutex.lock(el.io()) catch |err| switch (err) { |
| 228 | | error.Canceled => unreachable, // main fiber cannot be canceled |
| 229 | | }; |
| 230 | | defer el.detached.mutex.unlock(el.io()); |
| 231 | | const detached: *DetachedClosure = @fieldParentPtr( |
| 232 | | "detached_queue_node", |
| 233 | | el.detached.list.pop() orelse break, |
| 234 | | ); |
| 235 | | // notify the detached fiber that it is no longer allowed to recycle itself |
| 236 | | detached.detached_queue_node = .{ |
| 237 | | .prev = &detached.detached_queue_node, |
| 238 | | .next = &detached.detached_queue_node, |
| 239 | | }; |
| 240 | | break :detached_future @ptrCast(detached.fiber); |
| 241 | | }, &.{}, .@"1"); |
| 242 | 219 | const active_threads = @atomicLoad(u32, &el.threads.active, .acquire); |
| 243 | 220 | for (el.threads.allocated[0..active_threads]) |*thread| { |
| 244 | 221 | const ready_fiber = @atomicLoad(?*Fiber, &thread.ready_queue, .monotonic); |
| ... | ... | @@ -492,7 +469,7 @@ const SwitchMessage = struct { |
| 492 | 469 | const PendingTask = union(enum) { |
| 493 | 470 | nothing, |
| 494 | 471 | reschedule, |
| 495 | | recycle, |
| 472 | recycle: *Fiber, |
| 496 | 473 | register_awaiter: *?*Fiber, |
| 497 | 474 | register_select: []const *Io.AnyFuture, |
| 498 | 475 | mutex_lock: struct { |
| ... | ... | @@ -516,10 +493,8 @@ const SwitchMessage = struct { |
| 516 | 493 | assert(prev_fiber.queue_next == null); |
| 517 | 494 | el.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber }); |
| 518 | 495 | }, |
| 519 | | .recycle => { |
| 520 | | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| 521 | | assert(prev_fiber.queue_next == null); |
| 522 | | el.recycle(prev_fiber); |
| 496 | .recycle => |fiber| { |
| 497 | el.recycle(fiber); |
| 523 | 498 | }, |
| 524 | 499 | .register_awaiter => |awaiter| { |
| 525 | 500 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| ... | ... | @@ -829,12 +804,9 @@ fn fiberEntry() callconv(.naked) void { |
| 829 | 804 | switch (builtin.cpu.arch) { |
| 830 | 805 | .x86_64 => asm volatile ( |
| 831 | 806 | \\ leaq 8(%%rsp), %%rdi |
| 832 | | \\ jmpq *(%%rsp) |
| 833 | | ), |
| 834 | | .aarch64 => asm volatile ( |
| 835 | | \\ mov x0, sp |
| 836 | | \\ ldr x2, [sp, #-8] |
| 837 | | \\ br x2 |
| 807 | \\ jmp %[AsyncClosure_call:P] |
| 808 | : |
| 809 | : [AsyncClosure_call] "X" (&AsyncClosure.call), |
| 838 | 810 | ), |
| 839 | 811 | else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)), |
| 840 | 812 | } |
| ... | ... | @@ -905,18 +877,16 @@ fn concurrent( |
| 905 | 877 | std.log.debug("allocated {*}", .{fiber}); |
| 906 | 878 | |
| 907 | 879 | const closure: *AsyncClosure = .fromFiber(fiber); |
| 908 | | const stack_end: [*]align(16) usize = @ptrCast(@alignCast(closure)); |
| 909 | | (stack_end - 1)[0..1].* = .{@intFromPtr(&AsyncClosure.call)}; |
| 910 | 880 | fiber.* = .{ |
| 911 | 881 | .required_align = {}, |
| 912 | 882 | .context = switch (builtin.cpu.arch) { |
| 913 | 883 | .x86_64 => .{ |
| 914 | | .rsp = @intFromPtr(stack_end - 1), |
| 884 | .rsp = @intFromPtr(closure) - @sizeOf(usize), |
| 915 | 885 | .rbp = 0, |
| 916 | 886 | .rip = @intFromPtr(&fiberEntry), |
| 917 | 887 | }, |
| 918 | 888 | .aarch64 => .{ |
| 919 | | .sp = @intFromPtr(stack_end), |
| 889 | .sp = @intFromPtr(closure) - @sizeOf(usize) - 1, |
| 920 | 890 | .fp = 0, |
| 921 | 891 | .pc = @intFromPtr(&fiberEntry), |
| 922 | 892 | }, |
| ... | ... | @@ -968,70 +938,6 @@ const DetachedClosure = struct { |
| 968 | 938 | } |
| 969 | 939 | }; |
| 970 | 940 | |
| 971 | | fn asyncDetached( |
| 972 | | userdata: ?*anyopaque, |
| 973 | | context: []const u8, |
| 974 | | context_alignment: std.mem.Alignment, |
| 975 | | start: *const fn (context: *const anyopaque) void, |
| 976 | | ) void { |
| 977 | | assert(context_alignment.compare(.lte, Fiber.max_context_align)); // TODO |
| 978 | | assert(context.len <= Fiber.max_context_size); // TODO |
| 979 | | |
| 980 | | const event_loop: *EventLoop = @ptrCast(@alignCast(userdata)); |
| 981 | | const fiber = Fiber.allocate(event_loop) catch { |
| 982 | | start(context.ptr); |
| 983 | | return; |
| 984 | | }; |
| 985 | | std.log.debug("allocated {*}", .{fiber}); |
| 986 | | |
| 987 | | const current_thread: *Thread = .current(); |
| 988 | | const closure: *DetachedClosure = @ptrFromInt(Fiber.max_context_align.max(.of(DetachedClosure)).backward( |
| 989 | | @intFromPtr(fiber.allocatedEnd()) - Fiber.max_context_size, |
| 990 | | ) - @sizeOf(DetachedClosure)); |
| 991 | | const stack_end: [*]align(16) usize = @ptrCast(@alignCast(closure)); |
| 992 | | (stack_end - 1)[0..1].* = .{@intFromPtr(&DetachedClosure.call)}; |
| 993 | | fiber.* = .{ |
| 994 | | .required_align = {}, |
| 995 | | .context = switch (builtin.cpu.arch) { |
| 996 | | .x86_64 => .{ |
| 997 | | .rsp = @intFromPtr(stack_end - 1), |
| 998 | | .rbp = 0, |
| 999 | | .rip = @intFromPtr(&fiberEntry), |
| 1000 | | }, |
| 1001 | | .aarch64 => .{ |
| 1002 | | .sp = @intFromPtr(stack_end), |
| 1003 | | .fp = 0, |
| 1004 | | .pc = @intFromPtr(&fiberEntry), |
| 1005 | | }, |
| 1006 | | else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)), |
| 1007 | | }, |
| 1008 | | .awaiter = null, |
| 1009 | | .queue_next = null, |
| 1010 | | .cancel_thread = null, |
| 1011 | | .awaiting_completions = .initEmpty(), |
| 1012 | | }; |
| 1013 | | closure.* = .{ |
| 1014 | | .event_loop = event_loop, |
| 1015 | | .fiber = fiber, |
| 1016 | | .start = start, |
| 1017 | | .detached_queue_node = .{}, |
| 1018 | | }; |
| 1019 | | { |
| 1020 | | event_loop.detached.mutex.lock(event_loop.io()) catch |err| switch (err) { |
| 1021 | | error.Canceled => { |
| 1022 | | event_loop.recycle(fiber); |
| 1023 | | start(context.ptr); |
| 1024 | | return; |
| 1025 | | }, |
| 1026 | | }; |
| 1027 | | defer event_loop.detached.mutex.unlock(event_loop.io()); |
| 1028 | | event_loop.detached.list.append(&closure.detached_queue_node); |
| 1029 | | } |
| 1030 | | @memcpy(closure.contextPointer(), context); |
| 1031 | | |
| 1032 | | event_loop.schedule(current_thread, .{ .head = fiber, .tail = fiber }); |
| 1033 | | } |
| 1034 | | |
| 1035 | 941 | fn await( |
| 1036 | 942 | userdata: ?*anyopaque, |
| 1037 | 943 | any_future: *std.Io.AnyFuture, |