| ... | @@ -4,28 +4,23 @@ const assert = std.debug.assert; | ... | @@ -4,28 +4,23 @@ const assert = std.debug.assert; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | const Io = std.Io; | 5 | const Io = std.Io; |
| 6 | const EventLoop = @This(); | 6 | const EventLoop = @This(); |
| | 7 | const Alignment = std.mem.Alignment; |
| 7 | | 8 | |
| 8 | gpa: Allocator, | 9 | gpa: Allocator, |
| 9 | mutex: std.Thread.Mutex, | 10 | mutex: std.Thread.Mutex, |
| 10 | cond: std.Thread.Condition, | 11 | cond: std.Thread.Condition, |
| 11 | queue: std.DoublyLinkedList(void), | 12 | queue: std.DoublyLinkedList(void), |
| 12 | free: std.DoublyLinkedList(void), | 13 | free: std.DoublyLinkedList(void), |
| 13 | main_fiber_buffer: [@sizeOf(Fiber) + max_result_len]u8 align(@alignOf(Fiber)), | 14 | main_context: Context, |
| 14 | exit_awaiter: ?*Fiber, | 15 | exit_awaiter: ?*Fiber, |
| 15 | idle_count: usize, | 16 | idle_count: usize, |
| 16 | threads: std.ArrayListUnmanaged(Thread), | 17 | threads: std.ArrayListUnmanaged(Thread), |
| 17 | | 18 | |
| 18 | threadlocal var current_idle_context: *Context = undefined; | 19 | threadlocal var current_idle_context: *Context = undefined; |
| 19 | threadlocal var current_fiber_context: *Context = undefined; | 20 | threadlocal var current_context: *Context = undefined; |
| 20 | | 21 | |
| 21 | /// Also used for context. | 22 | /// Empirically saw 10KB being used by the self-hosted backend for logging. |
| 22 | const max_result_len = 64; | | |
| 23 | /// Also used for context. | | |
| 24 | const max_result_align: std.mem.Alignment = .@"16"; | | |
| 25 | | | |
| 26 | const min_stack_size = 4 * 1024 * 1024; | | |
| 27 | const idle_stack_size = 32 * 1024; | 23 | const idle_stack_size = 32 * 1024; |
| 28 | const stack_align = 16; | | |
| 29 | | 24 | |
| 30 | const Thread = struct { | 25 | const Thread = struct { |
| 31 | thread: std.Thread, | 26 | thread: std.Thread, |
| ... | @@ -33,45 +28,53 @@ const Thread = struct { | ... | @@ -33,45 +28,53 @@ const Thread = struct { |
| 33 | }; | 28 | }; |
| 34 | | 29 | |
| 35 | const Fiber = struct { | 30 | const Fiber = struct { |
| 36 | _: void align(max_result_align.toByteUnits()) = {}, | | |
| 37 | | | |
| 38 | context: Context, | 31 | context: Context, |
| 39 | awaiter: ?*Fiber, | 32 | awaiter: ?*Fiber, |
| 40 | queue_node: std.DoublyLinkedList(void).Node, | 33 | queue_node: std.DoublyLinkedList(void).Node, |
| | 34 | result_align: Alignment, |
| 41 | | 35 | |
| 42 | const finished: ?*Fiber = @ptrFromInt(std.mem.alignBackward(usize, std.math.maxInt(usize), @alignOf(Fiber))); | 36 | const finished: ?*Fiber = @ptrFromInt(std.mem.alignBackward(usize, std.math.maxInt(usize), @alignOf(Fiber))); |
| 43 | | 37 | |
| 44 | fn allocatedSlice(f: *Fiber) []align(@alignOf(Fiber)) u8 { | 38 | const max_result_align: Alignment = .@"16"; |
| 45 | const base: [*]align(@alignOf(Fiber)) u8 = @ptrCast(f); | 39 | const max_result_size = max_result_align.forward(64); |
| 46 | return base[0..std.mem.alignForward( | 40 | /// This includes any stack realignments that need to happen, and also the |
| | 41 | /// initial frame return address slot and argument frame, depending on target. |
| | 42 | const min_stack_size = 4 * 1024 * 1024; |
| | 43 | const max_context_align: Alignment = .@"16"; |
| | 44 | const max_context_size = max_context_align.forward(1024); |
| | 45 | const allocation_size = std.mem.alignForward( |
| | 46 | usize, |
| | 47 | std.mem.alignForward( |
| 47 | usize, | 48 | usize, |
| 48 | resultOffset() + max_result_len + min_stack_size, | 49 | max_result_align.forward(@sizeOf(Fiber)) + max_result_size + min_stack_size, |
| 49 | std.heap.page_size_max, | 50 | @max(@alignOf(AsyncClosure), max_context_align.toByteUnits()), |
| 50 | )]; | 51 | ) + @sizeOf(AsyncClosure) + max_context_size, |
| 51 | } | 52 | std.heap.page_size_max, |
| 52 | | 53 | ); |
| 53 | fn argsOffset() usize { | 54 | |
| 54 | return max_result_align.forward(@sizeOf(Fiber)); | 55 | fn allocate(el: *EventLoop) error{OutOfMemory}!*Fiber { |
| 55 | } | 56 | return if (free_node: { |
| 56 | | 57 | el.mutex.lock(); |
| 57 | fn resultOffset() usize { | 58 | defer el.mutex.unlock(); |
| 58 | return max_result_align.forward(argsOffset() + max_result_len); | 59 | break :free_node el.free.pop(); |
| 59 | } | 60 | }) |free_node| |
| 60 | | 61 | @alignCast(@fieldParentPtr("queue_node", free_node)) |
| 61 | fn argsSlice(f: *Fiber) []u8 { | 62 | else |
| 62 | const base: [*]align(@alignOf(Fiber)) u8 = @ptrCast(f); | 63 | @ptrCast(try el.gpa.alignedAlloc(u8, @alignOf(Fiber), allocation_size)); |
| 63 | return base[argsOffset()..][0..max_result_len]; | | |
| 64 | } | 64 | } |
| 65 | | 65 | |
| 66 | fn resultSlice(f: *Fiber) []u8 { | 66 | fn allocatedSlice(f: *Fiber) []align(@alignOf(Fiber)) u8 { |
| 67 | const base: [*]align(@alignOf(Fiber)) u8 = @ptrCast(f); | 67 | return @as([*]align(@alignOf(Fiber)) u8, @ptrCast(f))[0..allocation_size]; |
| 68 | return base[resultOffset()..][0..max_result_len]; | | |
| 69 | } | 68 | } |
| 70 | | 69 | |
| 71 | fn stackEndPointer(f: *Fiber) [*]u8 { | 70 | fn allocatedEnd(f: *Fiber) [*]u8 { |
| 72 | const allocated_slice = f.allocatedSlice(); | 71 | const allocated_slice = f.allocatedSlice(); |
| 73 | return allocated_slice[allocated_slice.len..].ptr; | 72 | return allocated_slice[allocated_slice.len..].ptr; |
| 74 | } | 73 | } |
| | 74 | |
| | 75 | fn resultPointer(f: *Fiber) [*]u8 { |
| | 76 | return @ptrFromInt(f.result_align.forward(@intFromPtr(f) + @sizeOf(Fiber))); |
| | 77 | } |
| 75 | }; | 78 | }; |
| 76 | | 79 | |
| 77 | pub fn io(el: *EventLoop) Io { | 80 | pub fn io(el: *EventLoop) Io { |
| ... | @@ -88,7 +91,7 @@ pub fn init(el: *EventLoop, gpa: Allocator) error{OutOfMemory}!void { | ... | @@ -88,7 +91,7 @@ pub fn init(el: *EventLoop, gpa: Allocator) error{OutOfMemory}!void { |
| 88 | const threads_bytes = ((std.Thread.getCpuCount() catch 1) -| 1) * @sizeOf(Thread); | 91 | const threads_bytes = ((std.Thread.getCpuCount() catch 1) -| 1) * @sizeOf(Thread); |
| 89 | const idle_context_offset = std.mem.alignForward(usize, threads_bytes, @alignOf(Context)); | 92 | const idle_context_offset = std.mem.alignForward(usize, threads_bytes, @alignOf(Context)); |
| 90 | const idle_stack_end_offset = std.mem.alignForward(usize, idle_context_offset + idle_stack_size, std.heap.page_size_max); | 93 | const idle_stack_end_offset = std.mem.alignForward(usize, idle_context_offset + idle_stack_size, std.heap.page_size_max); |
| 91 | const allocated_slice = try gpa.alignedAlloc(u8, @max(@alignOf(Thread), @alignOf(Context), stack_align), idle_stack_end_offset); | 94 | const allocated_slice = try gpa.alignedAlloc(u8, @max(@alignOf(Thread), @alignOf(Context)), idle_stack_end_offset); |
| 92 | errdefer gpa.free(allocated_slice); | 95 | errdefer gpa.free(allocated_slice); |
| 93 | el.* = .{ | 96 | el.* = .{ |
| 94 | .gpa = gpa, | 97 | .gpa = gpa, |
| ... | @@ -96,13 +99,13 @@ pub fn init(el: *EventLoop, gpa: Allocator) error{OutOfMemory}!void { | ... | @@ -96,13 +99,13 @@ pub fn init(el: *EventLoop, gpa: Allocator) error{OutOfMemory}!void { |
| 96 | .cond = .{}, | 99 | .cond = .{}, |
| 97 | .queue = .{}, | 100 | .queue = .{}, |
| 98 | .free = .{}, | 101 | .free = .{}, |
| 99 | .main_fiber_buffer = undefined, | 102 | .main_context = undefined, |
| 100 | .exit_awaiter = null, | 103 | .exit_awaiter = null, |
| 101 | .idle_count = 0, | 104 | .idle_count = 0, |
| 102 | .threads = .initBuffer(@ptrCast(allocated_slice[0..threads_bytes])), | 105 | .threads = .initBuffer(@ptrCast(allocated_slice[0..threads_bytes])), |
| 103 | }; | 106 | }; |
| 104 | const main_idle_context: *Context = @alignCast(std.mem.bytesAsValue(Context, allocated_slice[idle_context_offset..][0..@sizeOf(Context)])); | 107 | const main_idle_context: *Context = @alignCast(std.mem.bytesAsValue(Context, allocated_slice[idle_context_offset..][0..@sizeOf(Context)])); |
| 105 | const idle_stack_end: [*]align(stack_align) usize = @alignCast(@ptrCast(allocated_slice[idle_stack_end_offset..].ptr)); | 108 | const idle_stack_end: [*]align(@max(@alignOf(Thread), @alignOf(Context))) usize = @alignCast(@ptrCast(allocated_slice[idle_stack_end_offset..].ptr)); |
| 106 | (idle_stack_end - 1)[0..1].* = .{@intFromPtr(el)}; | 109 | (idle_stack_end - 1)[0..1].* = .{@intFromPtr(el)}; |
| 107 | main_idle_context.* = .{ | 110 | main_idle_context.* = .{ |
| 108 | .rsp = @intFromPtr(idle_stack_end - 1), | 111 | .rsp = @intFromPtr(idle_stack_end - 1), |
| ... | @@ -111,9 +114,8 @@ pub fn init(el: *EventLoop, gpa: Allocator) error{OutOfMemory}!void { | ... | @@ -111,9 +114,8 @@ pub fn init(el: *EventLoop, gpa: Allocator) error{OutOfMemory}!void { |
| 111 | }; | 114 | }; |
| 112 | std.log.debug("created main idle {*}", .{main_idle_context}); | 115 | std.log.debug("created main idle {*}", .{main_idle_context}); |
| 113 | current_idle_context = main_idle_context; | 116 | current_idle_context = main_idle_context; |
| 114 | const current_fiber: *Fiber = @ptrCast(&el.main_fiber_buffer); | 117 | std.log.debug("created main {*}", .{&el.main_context}); |
| 115 | std.log.debug("created main fiber {*}", .{current_fiber}); | 118 | current_context = &el.main_context; |
| 116 | current_fiber_context = &current_fiber.context; | | |
| 117 | } | 119 | } |
| 118 | | 120 | |
| 119 | pub fn deinit(el: *EventLoop) void { | 121 | pub fn deinit(el: *EventLoop) void { |
| ... | @@ -125,27 +127,11 @@ pub fn deinit(el: *EventLoop) void { | ... | @@ -125,27 +127,11 @@ pub fn deinit(el: *EventLoop) void { |
| 125 | } | 127 | } |
| 126 | const idle_context_offset = std.mem.alignForward(usize, el.threads.items.len * @sizeOf(Thread), @alignOf(Context)); | 128 | const idle_context_offset = std.mem.alignForward(usize, el.threads.items.len * @sizeOf(Thread), @alignOf(Context)); |
| 127 | const idle_stack_end = std.mem.alignForward(usize, idle_context_offset + idle_stack_size, std.heap.page_size_max); | 129 | const idle_stack_end = std.mem.alignForward(usize, idle_context_offset + idle_stack_size, std.heap.page_size_max); |
| 128 | const allocated_ptr: [*]align(@max(@alignOf(Thread), @alignOf(Context), stack_align)) u8 = @alignCast(@ptrCast(el.threads.items.ptr)); | 130 | const allocated_ptr: [*]align(@max(@alignOf(Thread), @alignOf(Context))) u8 = @alignCast(@ptrCast(el.threads.items.ptr)); |
| 129 | for (el.threads.items) |*thread| thread.thread.join(); | 131 | for (el.threads.items) |*thread| thread.thread.join(); |
| 130 | el.gpa.free(allocated_ptr[0..idle_stack_end]); | 132 | el.gpa.free(allocated_ptr[0..idle_stack_end]); |
| 131 | } | 133 | } |
| 132 | | 134 | |
| 133 | fn allocateFiber(el: *EventLoop) error{OutOfMemory}!*Fiber { | | |
| 134 | const free_node = free_node: { | | |
| 135 | el.mutex.lock(); | | |
| 136 | defer el.mutex.unlock(); | | |
| 137 | break :free_node el.free.pop(); | | |
| 138 | } orelse { | | |
| 139 | const n = std.mem.alignForward( | | |
| 140 | usize, | | |
| 141 | Fiber.resultOffset() + max_result_len + min_stack_size, | | |
| 142 | std.heap.page_size_max, | | |
| 143 | ); | | |
| 144 | return @alignCast(@ptrCast(try el.gpa.alignedAlloc(u8, @alignOf(Fiber), n))); | | |
| 145 | }; | | |
| 146 | return @alignCast(@fieldParentPtr("queue_node", free_node)); | | |
| 147 | } | | |
| 148 | | | |
| 149 | fn yield(el: *EventLoop, optional_fiber: ?*Fiber, register_awaiter: ?*?*Fiber) void { | 135 | fn yield(el: *EventLoop, optional_fiber: ?*Fiber, register_awaiter: ?*?*Fiber) void { |
| 150 | const ready_context: *Context = ready_context: { | 136 | const ready_context: *Context = ready_context: { |
| 151 | const ready_fiber: *Fiber = optional_fiber orelse if (ready_node: { | 137 | const ready_fiber: *Fiber = optional_fiber orelse if (ready_node: { |
| ... | @@ -159,7 +145,7 @@ fn yield(el: *EventLoop, optional_fiber: ?*Fiber, register_awaiter: ?*?*Fiber) v | ... | @@ -159,7 +145,7 @@ fn yield(el: *EventLoop, optional_fiber: ?*Fiber, register_awaiter: ?*?*Fiber) v |
| 159 | break :ready_context &ready_fiber.context; | 145 | break :ready_context &ready_fiber.context; |
| 160 | }; | 146 | }; |
| 161 | const message: SwitchMessage = .{ | 147 | const message: SwitchMessage = .{ |
| 162 | .prev_context = current_fiber_context, | 148 | .prev_context = current_context, |
| 163 | .ready_context = ready_context, | 149 | .ready_context = ready_context, |
| 164 | .register_awaiter = register_awaiter, | 150 | .register_awaiter = register_awaiter, |
| 165 | }; | 151 | }; |
| ... | @@ -189,14 +175,13 @@ fn schedule(el: *EventLoop, fiber: *Fiber) void { | ... | @@ -189,14 +175,13 @@ fn schedule(el: *EventLoop, fiber: *Fiber) void { |
| 189 | | 175 | |
| 190 | fn recycle(el: *EventLoop, fiber: *Fiber) void { | 176 | fn recycle(el: *EventLoop, fiber: *Fiber) void { |
| 191 | std.log.debug("recyling {*}", .{fiber}); | 177 | std.log.debug("recyling {*}", .{fiber}); |
| 192 | fiber.awaiter = undefined; | 178 | @memset(fiber.allocatedSlice(), undefined); |
| 193 | @memset(fiber.resultSlice(), undefined); | | |
| 194 | el.mutex.lock(); | 179 | el.mutex.lock(); |
| 195 | defer el.mutex.unlock(); | 180 | defer el.mutex.unlock(); |
| 196 | el.free.append(&fiber.queue_node); | 181 | el.free.append(&fiber.queue_node); |
| 197 | } | 182 | } |
| 198 | | 183 | |
| 199 | fn mainIdle(el: *EventLoop, message: *const SwitchMessage) callconv(.c) noreturn { | 184 | fn mainIdle(el: *EventLoop, message: *const SwitchMessage) callconv(.withStackAlign(.c, @max(@alignOf(Thread), @alignOf(Context)))) noreturn { |
| 200 | message.handle(el); | 185 | message.handle(el); |
| 201 | el.yield(el.idle(), null); | 186 | el.yield(el.idle(), null); |
| 202 | unreachable; // switched to dead fiber | 187 | unreachable; // switched to dead fiber |
| ... | @@ -205,7 +190,7 @@ fn mainIdle(el: *EventLoop, message: *const SwitchMessage) callconv(.c) noreturn | ... | @@ -205,7 +190,7 @@ fn mainIdle(el: *EventLoop, message: *const SwitchMessage) callconv(.c) noreturn |
| 205 | fn threadEntry(el: *EventLoop, thread: *Thread) void { | 190 | fn threadEntry(el: *EventLoop, thread: *Thread) void { |
| 206 | std.log.debug("created thread idle {*}", .{&thread.idle_context}); | 191 | std.log.debug("created thread idle {*}", .{&thread.idle_context}); |
| 207 | current_idle_context = &thread.idle_context; | 192 | current_idle_context = &thread.idle_context; |
| 208 | current_fiber_context = &thread.idle_context; | 193 | current_context = &thread.idle_context; |
| 209 | _ = el.idle(); | 194 | _ = el.idle(); |
| 210 | } | 195 | } |
| 211 | | 196 | |
| ... | @@ -230,7 +215,7 @@ const SwitchMessage = extern struct { | ... | @@ -230,7 +215,7 @@ const SwitchMessage = extern struct { |
| 230 | register_awaiter: ?*?*Fiber, | 215 | register_awaiter: ?*?*Fiber, |
| 231 | | 216 | |
| 232 | fn handle(message: *const SwitchMessage, el: *EventLoop) void { | 217 | fn handle(message: *const SwitchMessage, el: *EventLoop) void { |
| 233 | current_fiber_context = message.ready_context; | 218 | current_context = message.ready_context; |
| 234 | if (message.register_awaiter) |awaiter| { | 219 | if (message.register_awaiter) |awaiter| { |
| 235 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.prev_context)); | 220 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.prev_context)); |
| 236 | if (@atomicRmw(?*Fiber, awaiter, .Xchg, prev_fiber, .acq_rel) == Fiber.finished) el.schedule(prev_fiber); | 221 | if (@atomicRmw(?*Fiber, awaiter, .Xchg, prev_fiber, .acq_rel) == Fiber.finished) el.schedule(prev_fiber); |
| ... | @@ -238,10 +223,13 @@ const SwitchMessage = extern struct { | ... | @@ -238,10 +223,13 @@ const SwitchMessage = extern struct { |
| 238 | } | 223 | } |
| 239 | }; | 224 | }; |
| 240 | | 225 | |
| 241 | const Context = extern struct { | 226 | const Context = switch (builtin.cpu.arch) { |
| 242 | rsp: usize, | 227 | .x86_64 => extern struct { |
| 243 | rbp: usize, | 228 | rsp: u64, |
| 244 | rip: usize, | 229 | rbp: u64, |
| | 230 | rip: u64, |
| | 231 | }, |
| | 232 | else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)), |
| 245 | }; | 233 | }; |
| 246 | | 234 | |
| 247 | inline fn contextSwitch(message: *const SwitchMessage) *const SwitchMessage { | 235 | inline fn contextSwitch(message: *const SwitchMessage) *const SwitchMessage { |
| ... | @@ -299,42 +287,45 @@ fn fiberEntry() callconv(.naked) void { | ... | @@ -299,42 +287,45 @@ fn fiberEntry() callconv(.naked) void { |
| 299 | pub fn @"async"( | 287 | pub fn @"async"( |
| 300 | userdata: ?*anyopaque, | 288 | userdata: ?*anyopaque, |
| 301 | result: []u8, | 289 | result: []u8, |
| 302 | result_alignment: std.mem.Alignment, | 290 | result_alignment: Alignment, |
| 303 | context: []const u8, | 291 | context: []const u8, |
| 304 | context_alignment: std.mem.Alignment, | 292 | context_alignment: Alignment, |
| 305 | start: *const fn (context: *const anyopaque, result: *anyopaque) void, | 293 | start: *const fn (context: *const anyopaque, result: *anyopaque) void, |
| 306 | ) ?*std.Io.AnyFuture { | 294 | ) ?*std.Io.AnyFuture { |
| 307 | assert(result_alignment.compare(.lte, max_result_align)); // TODO | 295 | assert(result_alignment.compare(.lte, Fiber.max_result_align)); // TODO |
| 308 | assert(context_alignment.compare(.lte, max_result_align)); // TODO | 296 | assert(context_alignment.compare(.lte, Fiber.max_context_align)); // TODO |
| 309 | assert(result.len <= max_result_len); // TODO | 297 | assert(result.len <= Fiber.max_result_size); // TODO |
| 310 | assert(context.len <= max_result_len); // TODO | 298 | assert(context.len <= Fiber.max_context_size); // TODO |
| 311 | | 299 | |
| 312 | const event_loop: *EventLoop = @alignCast(@ptrCast(userdata)); | 300 | const event_loop: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 313 | const fiber = event_loop.allocateFiber() catch { | 301 | const fiber = Fiber.allocate(event_loop) catch { |
| 314 | start(context.ptr, result.ptr); | 302 | start(context.ptr, result.ptr); |
| 315 | return null; | 303 | return null; |
| 316 | }; | 304 | }; |
| 317 | fiber.awaiter = null; | | |
| 318 | fiber.queue_node = .{ .data = {} }; | | |
| 319 | @memcpy(fiber.argsSlice()[0..context.len], context); | | |
| 320 | std.log.debug("allocated {*}", .{fiber}); | 305 | std.log.debug("allocated {*}", .{fiber}); |
| 321 | | 306 | |
| 322 | const closure: *AsyncClosure = @ptrFromInt(std.mem.alignBackward( | 307 | const closure: *AsyncClosure = @ptrFromInt(Fiber.max_context_align.max(.of(AsyncClosure)).backward( |
| 323 | usize, | 308 | @intFromPtr(fiber.allocatedEnd()) - Fiber.max_context_size, |
| 324 | @intFromPtr(fiber.stackEndPointer() - @sizeOf(AsyncClosure)), | 309 | ) - @sizeOf(AsyncClosure)); |
| 325 | @max(@alignOf(AsyncClosure), stack_align), | 310 | fiber.* = .{ |
| 326 | )); | 311 | .context = switch (builtin.cpu.arch) { |
| | 312 | .x86_64 => .{ |
| | 313 | .rsp = @intFromPtr(closure) - @sizeOf(usize), |
| | 314 | .rbp = 0, |
| | 315 | .rip = @intFromPtr(&fiberEntry), |
| | 316 | }, |
| | 317 | else => |arch| @compileError("unimplemented architecture: " ++ @tagName(arch)), |
| | 318 | }, |
| | 319 | .awaiter = null, |
| | 320 | .queue_node = undefined, |
| | 321 | .result_align = result_alignment, |
| | 322 | }; |
| 327 | closure.* = .{ | 323 | closure.* = .{ |
| 328 | .event_loop = event_loop, | 324 | .event_loop = event_loop, |
| 329 | .fiber = fiber, | 325 | .fiber = fiber, |
| 330 | .start = start, | 326 | .start = start, |
| 331 | }; | 327 | }; |
| 332 | const stack_end: [*]align(stack_align) usize = @alignCast(@ptrCast(closure)); | 328 | @memcpy(closure.contextPointer(), context); |
| 333 | fiber.context = .{ | | |
| 334 | .rsp = @intFromPtr(stack_end - 1), | | |
| 335 | .rbp = 0, | | |
| 336 | .rip = @intFromPtr(&fiberEntry), | | |
| 337 | }; | | |
| 338 | | 329 | |
| 339 | event_loop.schedule(fiber); | 330 | event_loop.schedule(fiber); |
| 340 | return @ptrCast(fiber); | 331 | return @ptrCast(fiber); |
| ... | @@ -345,10 +336,14 @@ const AsyncClosure = struct { | ... | @@ -345,10 +336,14 @@ const AsyncClosure = struct { |
| 345 | fiber: *Fiber, | 336 | fiber: *Fiber, |
| 346 | start: *const fn (context: *const anyopaque, result: *anyopaque) void, | 337 | start: *const fn (context: *const anyopaque, result: *anyopaque) void, |
| 347 | | 338 | |
| 348 | fn call(closure: *AsyncClosure, message: *const SwitchMessage) callconv(.c) noreturn { | 339 | fn contextPointer(closure: *AsyncClosure) [*]align(Fiber.max_context_align.toByteUnits()) u8 { |
| | 340 | return @alignCast(@as([*]u8, @ptrCast(closure)) + @sizeOf(AsyncClosure)); |
| | 341 | } |
| | 342 | |
| | 343 | fn call(closure: *AsyncClosure, message: *const SwitchMessage) callconv(.withStackAlign(.c, @alignOf(AsyncClosure))) noreturn { |
| 349 | message.handle(closure.event_loop); | 344 | message.handle(closure.event_loop); |
| 350 | std.log.debug("{*} performing async", .{closure.fiber}); | 345 | std.log.debug("{*} performing async", .{closure.fiber}); |
| 351 | closure.start(closure.fiber.argsSlice().ptr, closure.fiber.resultSlice().ptr); | 346 | closure.start(closure.contextPointer(), closure.fiber.resultPointer()); |
| 352 | const awaiter = @atomicRmw(?*Fiber, &closure.fiber.awaiter, .Xchg, Fiber.finished, .acq_rel); | 347 | const awaiter = @atomicRmw(?*Fiber, &closure.fiber.awaiter, .Xchg, Fiber.finished, .acq_rel); |
| 353 | closure.event_loop.yield(awaiter, null); | 348 | closure.event_loop.yield(awaiter, null); |
| 354 | unreachable; // switched to dead fiber | 349 | unreachable; // switched to dead fiber |
| ... | @@ -358,8 +353,7 @@ const AsyncClosure = struct { | ... | @@ -358,8 +353,7 @@ const AsyncClosure = struct { |
| 358 | pub fn @"await"(userdata: ?*anyopaque, any_future: *std.Io.AnyFuture, result: []u8) void { | 353 | pub fn @"await"(userdata: ?*anyopaque, any_future: *std.Io.AnyFuture, result: []u8) void { |
| 359 | const event_loop: *EventLoop = @alignCast(@ptrCast(userdata)); | 354 | const event_loop: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 360 | const future_fiber: *Fiber = @alignCast(@ptrCast(any_future)); | 355 | const future_fiber: *Fiber = @alignCast(@ptrCast(any_future)); |
| 361 | const result_src = future_fiber.resultSlice()[0..result.len]; | | |
| 362 | if (@atomicLoad(?*Fiber, &future_fiber.awaiter, .acquire) != Fiber.finished) event_loop.yield(null, &future_fiber.awaiter); | 356 | if (@atomicLoad(?*Fiber, &future_fiber.awaiter, .acquire) != Fiber.finished) event_loop.yield(null, &future_fiber.awaiter); |
| 363 | @memcpy(result, result_src); | 357 | @memcpy(result, future_fiber.resultPointer()); |
| 364 | event_loop.recycle(future_fiber); | 358 | event_loop.recycle(future_fiber); |
| 365 | } | 359 | } |