| ... | @@ -10,12 +10,9 @@ const IoUring = std.os.linux.IoUring; | ... | @@ -10,12 +10,9 @@ const IoUring = std.os.linux.IoUring; |
| 10 | | 10 | |
| 11 | /// Must be a thread-safe allocator. | 11 | /// Must be a thread-safe allocator. |
| 12 | gpa: Allocator, | 12 | gpa: Allocator, |
| | 13 | mutex: std.Thread.Mutex, |
| 13 | main_fiber_buffer: [@sizeOf(Fiber) + Fiber.max_result_size]u8 align(@alignOf(Fiber)), | 14 | main_fiber_buffer: [@sizeOf(Fiber) + Fiber.max_result_size]u8 align(@alignOf(Fiber)), |
| 14 | threads: Thread.List, | 15 | threads: Thread.List, |
| 15 | detached: struct { | | |
| 16 | mutex: std.Io.Mutex, | | |
| 17 | list: std.DoublyLinkedList, | | |
| 18 | }, | | |
| 19 | | 16 | |
| 20 | /// Empirically saw >128KB being used by the self-hosted backend to panic. | 17 | /// Empirically saw >128KB being used by the self-hosted backend to panic. |
| 21 | const idle_stack_size = 256 * 1024; | 18 | const idle_stack_size = 256 * 1024; |
| ... | @@ -142,7 +139,6 @@ pub fn io(el: *EventLoop) Io { | ... | @@ -142,7 +139,6 @@ pub fn io(el: *EventLoop) Io { |
| 142 | .async = async, | 139 | .async = async, |
| 143 | .concurrent = concurrent, | 140 | .concurrent = concurrent, |
| 144 | .await = await, | 141 | .await = await, |
| 145 | .asyncDetached = asyncDetached, | | |
| 146 | .select = select, | 142 | .select = select, |
| 147 | .cancel = cancel, | 143 | .cancel = cancel, |
| 148 | .cancelRequested = cancelRequested, | 144 | .cancelRequested = cancelRequested, |
| ... | @@ -172,16 +168,13 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { | ... | @@ -172,16 +168,13 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { |
| 172 | errdefer gpa.free(allocated_slice); | 168 | errdefer gpa.free(allocated_slice); |
| 173 | el.* = .{ | 169 | el.* = .{ |
| 174 | .gpa = gpa, | 170 | .gpa = gpa, |
| | 171 | .mutex = .{}, |
| 175 | .main_fiber_buffer = undefined, | 172 | .main_fiber_buffer = undefined, |
| 176 | .threads = .{ | 173 | .threads = .{ |
| 177 | .allocated = @ptrCast(allocated_slice[0..threads_size]), | 174 | .allocated = @ptrCast(allocated_slice[0..threads_size]), |
| 178 | .reserved = 1, | 175 | .reserved = 1, |
| 179 | .active = 1, | 176 | .active = 1, |
| 180 | }, | 177 | }, |
| 181 | .detached = .{ | | |
| 182 | .mutex = .init, | | |
| 183 | .list = .{}, | | |
| 184 | }, | | |
| 185 | }; | 178 | }; |
| 186 | const main_fiber: *Fiber = @ptrCast(&el.main_fiber_buffer); | 179 | const main_fiber: *Fiber = @ptrCast(&el.main_fiber_buffer); |
| 187 | main_fiber.* = .{ | 180 | main_fiber.* = .{ |
| ... | @@ -223,22 +216,6 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { | ... | @@ -223,22 +216,6 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { |
| 223 | } | 216 | } |
| 224 | | 217 | |
| 225 | pub fn deinit(el: *EventLoop) void { | 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 | const active_threads = @atomicLoad(u32, &el.threads.active, .acquire); | 219 | const active_threads = @atomicLoad(u32, &el.threads.active, .acquire); |
| 243 | for (el.threads.allocated[0..active_threads]) |*thread| { | 220 | for (el.threads.allocated[0..active_threads]) |*thread| { |
| 244 | const ready_fiber = @atomicLoad(?*Fiber, &thread.ready_queue, .monotonic); | 221 | const ready_fiber = @atomicLoad(?*Fiber, &thread.ready_queue, .monotonic); |
| ... | @@ -492,7 +469,7 @@ const SwitchMessage = struct { | ... | @@ -492,7 +469,7 @@ const SwitchMessage = struct { |
| 492 | const PendingTask = union(enum) { | 469 | const PendingTask = union(enum) { |
| 493 | nothing, | 470 | nothing, |
| 494 | reschedule, | 471 | reschedule, |
| 495 | recycle, | 472 | recycle: *Fiber, |
| 496 | register_awaiter: *?*Fiber, | 473 | register_awaiter: *?*Fiber, |
| 497 | register_select: []const *Io.AnyFuture, | 474 | register_select: []const *Io.AnyFuture, |
| 498 | mutex_lock: struct { | 475 | mutex_lock: struct { |
| ... | @@ -516,10 +493,8 @@ const SwitchMessage = struct { | ... | @@ -516,10 +493,8 @@ const SwitchMessage = struct { |
| 516 | assert(prev_fiber.queue_next == null); | 493 | assert(prev_fiber.queue_next == null); |
| 517 | el.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber }); | 494 | el.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber }); |
| 518 | }, | 495 | }, |
| 519 | .recycle => { | 496 | .recycle => |fiber| { |
| 520 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); | 497 | el.recycle(fiber); |
| 521 | assert(prev_fiber.queue_next == null); | | |
| 522 | el.recycle(prev_fiber); | | |
| 523 | }, | 498 | }, |
| 524 | .register_awaiter => |awaiter| { | 499 | .register_awaiter => |awaiter| { |
| 525 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); | 500 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| ... | @@ -829,12 +804,9 @@ fn fiberEntry() callconv(.naked) void { | ... | @@ -829,12 +804,9 @@ fn fiberEntry() callconv(.naked) void { |
| 829 | switch (builtin.cpu.arch) { | 804 | switch (builtin.cpu.arch) { |
| 830 | .x86_64 => asm volatile ( | 805 | .x86_64 => asm volatile ( |
| 831 | \\ leaq 8(%%rsp), %%rdi | 806 | \\ leaq 8(%%rsp), %%rdi |
| 832 | \\ jmpq *(%%rsp) | 807 | \\ jmp %[AsyncClosure_call:P] |
| 833 | ), | 808 | : |
| 834 | .aarch64 => asm volatile ( | 809 | : [AsyncClosure_call] "X" (&AsyncClosure.call), |
| 835 | \\ mov x0, sp | | |
| 836 | \\ ldr x2, [sp, #-8] | | |
| 837 | \\ br x2 | | |
| 838 | ), | 810 | ), |
| 839 | else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)), | 811 | else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)), |
| 840 | } | 812 | } |
| ... | @@ -905,18 +877,16 @@ fn concurrent( | ... | @@ -905,18 +877,16 @@ fn concurrent( |
| 905 | std.log.debug("allocated {*}", .{fiber}); | 877 | std.log.debug("allocated {*}", .{fiber}); |
| 906 | | 878 | |
| 907 | const closure: *AsyncClosure = .fromFiber(fiber); | 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 | fiber.* = .{ | 880 | fiber.* = .{ |
| 911 | .required_align = {}, | 881 | .required_align = {}, |
| 912 | .context = switch (builtin.cpu.arch) { | 882 | .context = switch (builtin.cpu.arch) { |
| 913 | .x86_64 => .{ | 883 | .x86_64 => .{ |
| 914 | .rsp = @intFromPtr(stack_end - 1), | 884 | .rsp = @intFromPtr(closure) - @sizeOf(usize), |
| 915 | .rbp = 0, | 885 | .rbp = 0, |
| 916 | .rip = @intFromPtr(&fiberEntry), | 886 | .rip = @intFromPtr(&fiberEntry), |
| 917 | }, | 887 | }, |
| 918 | .aarch64 => .{ | 888 | .aarch64 => .{ |
| 919 | .sp = @intFromPtr(stack_end), | 889 | .sp = @intFromPtr(closure) - @sizeOf(usize) - 1, |
| 920 | .fp = 0, | 890 | .fp = 0, |
| 921 | .pc = @intFromPtr(&fiberEntry), | 891 | .pc = @intFromPtr(&fiberEntry), |
| 922 | }, | 892 | }, |
| ... | @@ -968,70 +938,6 @@ const DetachedClosure = struct { | ... | @@ -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 | fn await( | 941 | fn await( |
| 1036 | userdata: ?*anyopaque, | 942 | userdata: ?*anyopaque, |
| 1037 | any_future: *std.Io.AnyFuture, | 943 | any_future: *std.Io.AnyFuture, |