| ... | ... | @@ -10,7 +10,7 @@ const IoUring = std.os.linux.IoUring; |
| 10 | 10 | /// Must be a thread-safe allocator. |
| 11 | 11 | gpa: Allocator, |
| 12 | 12 | mutex: std.Thread.Mutex, |
| 13 | | main_fiber: Fiber, |
| 13 | main_fiber_buffer: [@sizeOf(Fiber) + Fiber.max_result_size]u8 align(@alignOf(Fiber)), |
| 14 | 14 | threads: Thread.List, |
| 15 | 15 | |
| 16 | 16 | /// Empirically saw >128KB being used by the self-hosted backend to panic. |
| ... | ... | @@ -51,10 +51,12 @@ const Thread = struct { |
| 51 | 51 | }; |
| 52 | 52 | |
| 53 | 53 | const Fiber = struct { |
| 54 | required_align: void align(4), |
| 54 | 55 | context: Context, |
| 55 | 56 | awaiter: ?*Fiber, |
| 56 | 57 | queue_next: ?*Fiber, |
| 57 | 58 | cancel_thread: ?*Thread, |
| 59 | awaiting_completions: std.StaticBitSet(3), |
| 58 | 60 | |
| 59 | 61 | const finished: ?*Fiber = @ptrFromInt(@alignOf(Thread)); |
| 60 | 62 | |
| ... | ... | @@ -131,7 +133,7 @@ const Fiber = struct { |
| 131 | 133 | const thread: *Thread = .current(); |
| 132 | 134 | std.log.debug("recyling {*}", .{fiber}); |
| 133 | 135 | assert(fiber.queue_next == null); |
| 134 | | @memset(fiber.allocatedSlice(), undefined); |
| 136 | //@memset(fiber.allocatedSlice(), undefined); // (race) |
| 135 | 137 | fiber.queue_next = thread.free_queue; |
| 136 | 138 | thread.free_queue = fiber; |
| 137 | 139 | } |
| ... | ... | @@ -145,10 +147,17 @@ pub fn io(el: *EventLoop) Io { |
| 145 | 147 | .vtable = &.{ |
| 146 | 148 | .@"async" = @"async", |
| 147 | 149 | .@"await" = @"await", |
| 150 | .go = go, |
| 148 | 151 | |
| 149 | 152 | .cancel = cancel, |
| 150 | 153 | .cancelRequested = cancelRequested, |
| 151 | 154 | |
| 155 | .mutexLock = mutexLock, |
| 156 | .mutexUnlock = mutexUnlock, |
| 157 | |
| 158 | .conditionWait = conditionWait, |
| 159 | .conditionWake = conditionWake, |
| 160 | |
| 152 | 161 | .createFile = createFile, |
| 153 | 162 | .openFile = openFile, |
| 154 | 163 | .closeFile = closeFile, |
| ... | ... | @@ -169,18 +178,22 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { |
| 169 | 178 | el.* = .{ |
| 170 | 179 | .gpa = gpa, |
| 171 | 180 | .mutex = .{}, |
| 172 | | .main_fiber = .{ |
| 173 | | .context = undefined, |
| 174 | | .awaiter = null, |
| 175 | | .queue_next = null, |
| 176 | | .cancel_thread = null, |
| 177 | | }, |
| 181 | .main_fiber_buffer = undefined, |
| 178 | 182 | .threads = .{ |
| 179 | 183 | .allocated = @ptrCast(allocated_slice[0..threads_size]), |
| 180 | 184 | .reserved = 1, |
| 181 | 185 | .active = 1, |
| 182 | 186 | }, |
| 183 | 187 | }; |
| 188 | const main_fiber: *Fiber = @ptrCast(&el.main_fiber_buffer); |
| 189 | main_fiber.* = .{ |
| 190 | .required_align = {}, |
| 191 | .context = undefined, |
| 192 | .awaiter = null, |
| 193 | .queue_next = null, |
| 194 | .cancel_thread = null, |
| 195 | .awaiting_completions = .initEmpty(), |
| 196 | }; |
| 184 | 197 | const main_thread = &el.threads.allocated[0]; |
| 185 | 198 | Thread.self = main_thread; |
| 186 | 199 | const idle_stack_end: [*]usize = @alignCast(@ptrCast(allocated_slice[idle_stack_end_offset..].ptr)); |
| ... | ... | @@ -192,7 +205,7 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { |
| 192 | 205 | .rbp = 0, |
| 193 | 206 | .rip = @intFromPtr(&mainIdleEntry), |
| 194 | 207 | }, |
| 195 | | .current_context = &el.main_fiber.context, |
| 208 | .current_context = &main_fiber.context, |
| 196 | 209 | .ready_queue = null, |
| 197 | 210 | .free_queue = null, |
| 198 | 211 | .io_uring = try IoUring.init(io_uring_entries, 0), |
| ... | ... | @@ -201,53 +214,57 @@ pub fn init(el: *EventLoop, gpa: Allocator) !void { |
| 201 | 214 | }; |
| 202 | 215 | errdefer main_thread.io_uring.deinit(); |
| 203 | 216 | std.log.debug("created main idle {*}", .{&main_thread.idle_context}); |
| 204 | | std.log.debug("created main {*}", .{&el.main_fiber}); |
| 217 | std.log.debug("created main {*}", .{main_fiber}); |
| 205 | 218 | } |
| 206 | 219 | |
| 207 | 220 | pub fn deinit(el: *EventLoop) void { |
| 208 | 221 | const active_threads = @atomicLoad(u32, &el.threads.active, .acquire); |
| 209 | | for (el.threads.allocated[0..active_threads]) |*thread| |
| 210 | | assert(@atomicLoad(?*Fiber, &thread.ready_queue, .acquire) == null); // pending async |
| 222 | for (el.threads.allocated[0..active_threads]) |*thread| { |
| 223 | const ready_fiber = @atomicLoad(?*Fiber, &thread.ready_queue, .monotonic); |
| 224 | assert(ready_fiber == null or ready_fiber == Fiber.finished); // pending async |
| 225 | } |
| 211 | 226 | el.yield(null, .exit); |
| 227 | const allocated_ptr: [*]align(@alignOf(Thread)) u8 = @alignCast(@ptrCast(el.threads.allocated.ptr)); |
| 228 | const idle_stack_end_offset = std.mem.alignForward(usize, el.threads.allocated.len * @sizeOf(Thread) + idle_stack_size, std.heap.page_size_max); |
| 229 | for (el.threads.allocated[1..active_threads]) |*thread| thread.thread.join(); |
| 212 | 230 | for (el.threads.allocated[0..active_threads]) |*thread| while (thread.free_queue) |free_fiber| { |
| 213 | 231 | thread.free_queue = free_fiber.queue_next; |
| 214 | 232 | free_fiber.queue_next = null; |
| 215 | 233 | el.gpa.free(free_fiber.allocatedSlice()); |
| 216 | 234 | }; |
| 217 | | const allocated_ptr: [*]align(@alignOf(Thread)) u8 = @alignCast(@ptrCast(el.threads.allocated.ptr)); |
| 218 | | const idle_stack_end_offset = std.mem.alignForward(usize, el.threads.allocated.len * @sizeOf(Thread) + idle_stack_size, std.heap.page_size_max); |
| 219 | | for (el.threads.allocated[1..active_threads]) |thread| thread.thread.join(); |
| 220 | 235 | el.gpa.free(allocated_ptr[0..idle_stack_end_offset]); |
| 221 | 236 | el.* = undefined; |
| 222 | 237 | } |
| 223 | 238 | |
| 239 | fn findReadyFiber(el: *EventLoop, thread: *Thread) ?*Fiber { |
| 240 | if (@atomicRmw(?*Fiber, &thread.ready_queue, .Xchg, Fiber.finished, .acquire)) |ready_fiber| { |
| 241 | @atomicStore(?*Fiber, &thread.ready_queue, ready_fiber.queue_next, .release); |
| 242 | ready_fiber.queue_next = null; |
| 243 | return ready_fiber; |
| 244 | } |
| 245 | const active_threads = @atomicLoad(u32, &el.threads.active, .acquire); |
| 246 | for (0..@min(max_steal_ready_search, active_threads)) |_| { |
| 247 | defer thread.steal_ready_search_index += 1; |
| 248 | if (thread.steal_ready_search_index == active_threads) thread.steal_ready_search_index = 0; |
| 249 | const steal_ready_search_thread = &el.threads.allocated[0..active_threads][thread.steal_ready_search_index]; |
| 250 | if (steal_ready_search_thread == thread) continue; |
| 251 | const ready_fiber = @atomicRmw(?*Fiber, &steal_ready_search_thread.ready_queue, .And, Fiber.finished, .acquire) orelse continue; |
| 252 | if (ready_fiber == Fiber.finished) continue; |
| 253 | @atomicStore(?*Fiber, &thread.ready_queue, ready_fiber.queue_next, .release); |
| 254 | ready_fiber.queue_next = null; |
| 255 | return ready_fiber; |
| 256 | } |
| 257 | // couldn't find anything to do, so we are now open for business |
| 258 | @atomicStore(?*Fiber, &thread.ready_queue, null, .monotonic); |
| 259 | return null; |
| 260 | } |
| 261 | |
| 224 | 262 | fn yield(el: *EventLoop, maybe_ready_fiber: ?*Fiber, pending_task: SwitchMessage.PendingTask) void { |
| 225 | 263 | const thread: *Thread = .current(); |
| 226 | | const ready_context: *Context = if (maybe_ready_fiber) |ready_fiber| |
| 264 | const ready_context = if (maybe_ready_fiber orelse el.findReadyFiber(thread)) |ready_fiber| |
| 227 | 265 | &ready_fiber.context |
| 228 | | else if (thread.ready_queue) |ready_fiber| ready_context: { |
| 229 | | thread.ready_queue = ready_fiber.queue_next; |
| 230 | | ready_fiber.queue_next = null; |
| 231 | | break :ready_context &ready_fiber.context; |
| 232 | | } else ready_context: { |
| 233 | | const ready_threads = @atomicLoad(u32, &el.threads.active, .acquire); |
| 234 | | break :ready_context for (0..max_steal_ready_search) |_| { |
| 235 | | defer thread.steal_ready_search_index += 1; |
| 236 | | if (thread.steal_ready_search_index == ready_threads) thread.steal_ready_search_index = 0; |
| 237 | | const steal_ready_search_thread = &el.threads.allocated[thread.steal_ready_search_index]; |
| 238 | | if (steal_ready_search_thread == thread) continue; |
| 239 | | const ready_fiber = @atomicLoad(?*Fiber, &steal_ready_search_thread.ready_queue, .acquire) orelse continue; |
| 240 | | if (@cmpxchgWeak( |
| 241 | | ?*Fiber, |
| 242 | | &steal_ready_search_thread.ready_queue, |
| 243 | | ready_fiber, |
| 244 | | @atomicLoad(?*Fiber, &ready_fiber.queue_next, .acquire), |
| 245 | | .acq_rel, |
| 246 | | .monotonic, |
| 247 | | )) |_| continue; |
| 248 | | break &ready_fiber.context; |
| 249 | | } else &thread.idle_context; |
| 250 | | }; |
| 266 | else |
| 267 | &thread.idle_context; |
| 251 | 268 | const message: SwitchMessage = .{ |
| 252 | 269 | .contexts = .{ |
| 253 | 270 | .prev = thread.current_context, |
| ... | ... | @@ -270,10 +287,10 @@ fn schedule(el: *EventLoop, thread: *Thread, ready_queue: Fiber.Queue) void { |
| 270 | 287 | } |
| 271 | 288 | // shared fields of previous `Thread` must be initialized before later ones are marked as active |
| 272 | 289 | const new_thread_index = @atomicLoad(u32, &el.threads.active, .acquire); |
| 273 | | for (0..max_idle_search) |_| { |
| 290 | for (0..@min(max_idle_search, new_thread_index)) |_| { |
| 274 | 291 | defer thread.idle_search_index += 1; |
| 275 | 292 | if (thread.idle_search_index == new_thread_index) thread.idle_search_index = 0; |
| 276 | | const idle_search_thread = &el.threads.allocated[thread.idle_search_index]; |
| 293 | const idle_search_thread = &el.threads.allocated[0..new_thread_index][thread.idle_search_index]; |
| 277 | 294 | if (idle_search_thread == thread) continue; |
| 278 | 295 | if (@cmpxchgWeak( |
| 279 | 296 | ?*Fiber, |
| ... | ... | @@ -325,8 +342,8 @@ fn schedule(el: *EventLoop, thread: *Thread, ready_queue: Fiber.Queue) void { |
| 325 | 342 | std.log.warn("unable to create worker thread due to io_uring init failure: {s}", .{@errorName(err)}); |
| 326 | 343 | break :spawn_thread; |
| 327 | 344 | }, |
| 328 | | .idle_search_index = next_thread_index, |
| 329 | | .steal_ready_search_index = next_thread_index, |
| 345 | .idle_search_index = 0, |
| 346 | .steal_ready_search_index = 0, |
| 330 | 347 | }; |
| 331 | 348 | new_thread.thread = std.Thread.spawn(.{ |
| 332 | 349 | .stack_size = idle_stack_size, |
| ... | ... | @@ -357,7 +374,7 @@ fn mainIdle(el: *EventLoop, message: *const SwitchMessage) callconv(.withStackAl |
| 357 | 374 | message.handle(el); |
| 358 | 375 | const thread: *Thread = &el.threads.allocated[0]; |
| 359 | 376 | el.idle(thread); |
| 360 | | el.yield(&el.main_fiber, .nothing); |
| 377 | el.yield(@ptrCast(&el.main_fiber_buffer), .nothing); |
| 361 | 378 | unreachable; // switched to dead fiber |
| 362 | 379 | } |
| 363 | 380 | |
| ... | ... | @@ -384,8 +401,10 @@ const Completion = struct { |
| 384 | 401 | fn idle(el: *EventLoop, thread: *Thread) void { |
| 385 | 402 | var maybe_ready_fiber: ?*Fiber = null; |
| 386 | 403 | while (true) { |
| 387 | | el.yield(maybe_ready_fiber, .nothing); |
| 388 | | maybe_ready_fiber = null; |
| 404 | while (maybe_ready_fiber orelse el.findReadyFiber(thread)) |ready_fiber| { |
| 405 | el.yield(ready_fiber, .nothing); |
| 406 | maybe_ready_fiber = null; |
| 407 | } |
| 389 | 408 | _ = thread.io_uring.submit_and_wait(1) catch |err| switch (err) { |
| 390 | 409 | error.SignalInterrupt => std.log.warn("submit_and_wait failed with SignalInterrupt", .{}), |
| 391 | 410 | else => |e| @panic(@errorName(e)), |
| ... | ... | @@ -450,7 +469,12 @@ const SwitchMessage = struct { |
| 450 | 469 | |
| 451 | 470 | const PendingTask = union(enum) { |
| 452 | 471 | nothing, |
| 472 | reschedule, |
| 453 | 473 | register_awaiter: *?*Fiber, |
| 474 | lock_mutex: struct { |
| 475 | prev_state: Io.Mutex.State, |
| 476 | mutex: *Io.Mutex, |
| 477 | }, |
| 454 | 478 | exit, |
| 455 | 479 | }; |
| 456 | 480 | |
| ... | ... | @@ -459,8 +483,14 @@ const SwitchMessage = struct { |
| 459 | 483 | thread.current_context = message.contexts.ready; |
| 460 | 484 | switch (message.pending_task) { |
| 461 | 485 | .nothing => {}, |
| 486 | .reschedule => { |
| 487 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| 488 | assert(prev_fiber.queue_next == null); |
| 489 | el.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber }); |
| 490 | }, |
| 462 | 491 | .register_awaiter => |awaiter| { |
| 463 | 492 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| 493 | assert(prev_fiber.queue_next == null); |
| 464 | 494 | if (@atomicRmw( |
| 465 | 495 | ?*Fiber, |
| 466 | 496 | awaiter, |
| ... | ... | @@ -469,6 +499,36 @@ const SwitchMessage = struct { |
| 469 | 499 | .acq_rel, |
| 470 | 500 | ) == Fiber.finished) el.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber }); |
| 471 | 501 | }, |
| 502 | .lock_mutex => |lock_mutex| { |
| 503 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| 504 | assert(prev_fiber.queue_next == null); |
| 505 | var prev_state = lock_mutex.prev_state; |
| 506 | while (switch (prev_state) { |
| 507 | else => next_state: { |
| 508 | prev_fiber.queue_next = @ptrFromInt(@intFromEnum(prev_state)); |
| 509 | break :next_state @cmpxchgWeak( |
| 510 | Io.Mutex.State, |
| 511 | &lock_mutex.mutex.state, |
| 512 | prev_state, |
| 513 | @enumFromInt(@intFromPtr(prev_fiber)), |
| 514 | .release, |
| 515 | .acquire, |
| 516 | ); |
| 517 | }, |
| 518 | .unlocked => @cmpxchgWeak( |
| 519 | Io.Mutex.State, |
| 520 | &lock_mutex.mutex.state, |
| 521 | .unlocked, |
| 522 | .locked_once, |
| 523 | .acquire, |
| 524 | .acquire, |
| 525 | ) orelse { |
| 526 | prev_fiber.queue_next = null; |
| 527 | el.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber }); |
| 528 | return; |
| 529 | }, |
| 530 | }) |next_state| prev_state = next_state; |
| 531 | }, |
| 472 | 532 | .exit => for (el.threads.allocated[0..@atomicLoad(u32, &el.threads.active, .acquire)]) |*each_thread| { |
| 473 | 533 | getSqe(&thread.io_uring).* = .{ |
| 474 | 534 | .opcode = .MSG_RING, |
| ... | ... | @@ -590,13 +650,13 @@ fn @"async"( |
| 590 | 650 | start(context.ptr, result.ptr); |
| 591 | 651 | return null; |
| 592 | 652 | }; |
| 593 | | errdefer fiber.recycle(); |
| 594 | 653 | std.log.debug("allocated {*}", .{fiber}); |
| 595 | 654 | |
| 596 | 655 | const closure: *AsyncClosure = @ptrFromInt(Fiber.max_context_align.max(.of(AsyncClosure)).backward( |
| 597 | 656 | @intFromPtr(fiber.allocatedEnd()) - Fiber.max_context_size, |
| 598 | 657 | ) - @sizeOf(AsyncClosure)); |
| 599 | 658 | fiber.* = .{ |
| 659 | .required_align = {}, |
| 600 | 660 | .context = switch (builtin.cpu.arch) { |
| 601 | 661 | .x86_64 => .{ |
| 602 | 662 | .rsp = @intFromPtr(closure) - @sizeOf(usize), |
| ... | ... | @@ -608,6 +668,7 @@ fn @"async"( |
| 608 | 668 | .awaiter = null, |
| 609 | 669 | .queue_next = null, |
| 610 | 670 | .cancel_thread = null, |
| 671 | .awaiting_completions = .initEmpty(), |
| 611 | 672 | }; |
| 612 | 673 | closure.* = .{ |
| 613 | 674 | .event_loop = event_loop, |
| ... | ... | @@ -634,6 +695,19 @@ fn @"await"( |
| 634 | 695 | future_fiber.recycle(); |
| 635 | 696 | } |
| 636 | 697 | |
| 698 | fn go( |
| 699 | userdata: ?*anyopaque, |
| 700 | context: []const u8, |
| 701 | context_alignment: std.mem.Alignment, |
| 702 | start: *const fn (context: *const anyopaque) void, |
| 703 | ) void { |
| 704 | _ = userdata; |
| 705 | _ = context; |
| 706 | _ = context_alignment; |
| 707 | _ = start; |
| 708 | @panic("TODO"); |
| 709 | } |
| 710 | |
| 637 | 711 | fn cancel( |
| 638 | 712 | userdata: ?*anyopaque, |
| 639 | 713 | any_future: *std.Io.AnyFuture, |
| ... | ... | @@ -673,7 +747,7 @@ fn cancelRequested(userdata: ?*anyopaque) bool { |
| 673 | 747 | return @atomicLoad(?*Thread, &Thread.current().currentFiber().cancel_thread, .acquire) == Thread.canceling; |
| 674 | 748 | } |
| 675 | 749 | |
| 676 | | pub fn createFile( |
| 750 | fn createFile( |
| 677 | 751 | userdata: ?*anyopaque, |
| 678 | 752 | dir: std.fs.Dir, |
| 679 | 753 | sub_path: []const u8, |
| ... | ... | @@ -775,7 +849,7 @@ pub fn createFile( |
| 775 | 849 | } |
| 776 | 850 | } |
| 777 | 851 | |
| 778 | | pub fn openFile( |
| 852 | fn openFile( |
| 779 | 853 | userdata: ?*anyopaque, |
| 780 | 854 | dir: std.fs.Dir, |
| 781 | 855 | sub_path: []const u8, |
| ... | ... | @@ -883,7 +957,7 @@ pub fn openFile( |
| 883 | 957 | } |
| 884 | 958 | } |
| 885 | 959 | |
| 886 | | pub fn closeFile(userdata: ?*anyopaque, file: std.fs.File) void { |
| 960 | fn closeFile(userdata: ?*anyopaque, file: std.fs.File) void { |
| 887 | 961 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 888 | 962 | const thread: *Thread = .current(); |
| 889 | 963 | const iou = &thread.io_uring; |
| ... | ... | @@ -919,7 +993,7 @@ pub fn closeFile(userdata: ?*anyopaque, file: std.fs.File) void { |
| 919 | 993 | } |
| 920 | 994 | } |
| 921 | 995 | |
| 922 | | pub fn pread(userdata: ?*anyopaque, file: std.fs.File, buffer: []u8, offset: std.posix.off_t) Io.FilePReadError!usize { |
| 996 | fn pread(userdata: ?*anyopaque, file: std.fs.File, buffer: []u8, offset: std.posix.off_t) Io.FilePReadError!usize { |
| 923 | 997 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 924 | 998 | const thread: *Thread = .current(); |
| 925 | 999 | const iou = &thread.io_uring; |
| ... | ... | @@ -971,7 +1045,7 @@ pub fn pread(userdata: ?*anyopaque, file: std.fs.File, buffer: []u8, offset: std |
| 971 | 1045 | } |
| 972 | 1046 | } |
| 973 | 1047 | |
| 974 | | pub fn pwrite(userdata: ?*anyopaque, file: std.fs.File, buffer: []const u8, offset: std.posix.off_t) Io.FilePWriteError!usize { |
| 1048 | fn pwrite(userdata: ?*anyopaque, file: std.fs.File, buffer: []const u8, offset: std.posix.off_t) Io.FilePWriteError!usize { |
| 975 | 1049 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 976 | 1050 | const thread: *Thread = .current(); |
| 977 | 1051 | const iou = &thread.io_uring; |
| ... | ... | @@ -1027,13 +1101,13 @@ pub fn pwrite(userdata: ?*anyopaque, file: std.fs.File, buffer: []const u8, offs |
| 1027 | 1101 | } |
| 1028 | 1102 | } |
| 1029 | 1103 | |
| 1030 | | pub fn now(userdata: ?*anyopaque, clockid: std.posix.clockid_t) Io.ClockGetTimeError!Io.Timestamp { |
| 1104 | fn now(userdata: ?*anyopaque, clockid: std.posix.clockid_t) Io.ClockGetTimeError!Io.Timestamp { |
| 1031 | 1105 | _ = userdata; |
| 1032 | 1106 | const timespec = try std.posix.clock_gettime(clockid); |
| 1033 | 1107 | return @enumFromInt(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec); |
| 1034 | 1108 | } |
| 1035 | 1109 | |
| 1036 | | pub fn sleep(userdata: ?*anyopaque, clockid: std.posix.clockid_t, deadline: Io.Deadline) Io.SleepError!void { |
| 1110 | fn sleep(userdata: ?*anyopaque, clockid: std.posix.clockid_t, deadline: Io.Deadline) Io.SleepError!void { |
| 1037 | 1111 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 1038 | 1112 | const thread: *Thread = .current(); |
| 1039 | 1113 | const iou = &thread.io_uring; |
| ... | ... | @@ -1086,10 +1160,65 @@ pub fn sleep(userdata: ?*anyopaque, clockid: std.posix.clockid_t, deadline: Io.D |
| 1086 | 1160 | } |
| 1087 | 1161 | } |
| 1088 | 1162 | |
| 1163 | fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) error{Canceled}!void { |
| 1164 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 1165 | el.yield(null, .{ .lock_mutex = .{ |
| 1166 | .prev_state = prev_state, |
| 1167 | .mutex = mutex, |
| 1168 | } }); |
| 1169 | } |
| 1170 | fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { |
| 1171 | var maybe_waiting_fiber: ?*Fiber = @ptrFromInt(@intFromEnum(prev_state)); |
| 1172 | while (if (maybe_waiting_fiber) |waiting_fiber| @cmpxchgWeak( |
| 1173 | Io.Mutex.State, |
| 1174 | &mutex.state, |
| 1175 | @enumFromInt(@intFromPtr(waiting_fiber)), |
| 1176 | @enumFromInt(@intFromPtr(waiting_fiber.queue_next)), |
| 1177 | .release, |
| 1178 | .acquire, |
| 1179 | ) else @cmpxchgWeak( |
| 1180 | Io.Mutex.State, |
| 1181 | &mutex.state, |
| 1182 | .locked_once, |
| 1183 | .unlocked, |
| 1184 | .release, |
| 1185 | .acquire, |
| 1186 | ) orelse return) |next_state| maybe_waiting_fiber = @ptrFromInt(@intFromEnum(next_state)); |
| 1187 | maybe_waiting_fiber.?.queue_next = null; |
| 1188 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); |
| 1189 | el.yield(maybe_waiting_fiber.?, .reschedule); |
| 1190 | } |
| 1191 | |
| 1192 | fn conditionWait( |
| 1193 | userdata: ?*anyopaque, |
| 1194 | cond: *Io.Condition, |
| 1195 | mutex: *Io.Mutex, |
| 1196 | timeout: ?u64, |
| 1197 | ) Io.Condition.WaitError!void { |
| 1198 | _ = userdata; |
| 1199 | _ = cond; |
| 1200 | _ = mutex; |
| 1201 | _ = timeout; |
| 1202 | @panic("TODO"); |
| 1203 | } |
| 1204 | |
| 1205 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, notify: Io.Condition.Notify) void { |
| 1206 | _ = userdata; |
| 1207 | _ = cond; |
| 1208 | _ = notify; |
| 1209 | @panic("TODO"); |
| 1210 | } |
| 1211 | |
| 1089 | 1212 | fn errno(signed: i32) std.os.linux.E { |
| 1090 | 1213 | return .init(@bitCast(@as(isize, signed))); |
| 1091 | 1214 | } |
| 1092 | 1215 | |
| 1093 | 1216 | fn getSqe(iou: *IoUring) *std.os.linux.io_uring_sqe { |
| 1094 | | return iou.get_sqe() catch @panic("TODO: handle submission queue full"); |
| 1217 | while (true) return iou.get_sqe() catch { |
| 1218 | _ = iou.submit_and_wait(0) catch |err| switch (err) { |
| 1219 | error.SignalInterrupt => std.log.warn("submit_and_wait failed with SignalInterrupt", .{}), |
| 1220 | else => |e| @panic(@errorName(e)), |
| 1221 | }; |
| 1222 | continue; |
| 1223 | }; |
| 1095 | 1224 | } |