| ... | @@ -485,6 +485,7 @@ const SwitchMessage = struct { | ... | @@ -485,6 +485,7 @@ const SwitchMessage = struct { |
| 485 | reschedule, | 485 | reschedule, |
| 486 | recycle, | 486 | recycle, |
| 487 | register_awaiter: *?*Fiber, | 487 | register_awaiter: *?*Fiber, |
| | 488 | register_select: []const *Io.AnyFuture, |
| 488 | mutex_lock: struct { | 489 | mutex_lock: struct { |
| 489 | prev_state: Io.Mutex.State, | 490 | prev_state: Io.Mutex.State, |
| 490 | mutex: *Io.Mutex, | 491 | mutex: *Io.Mutex, |
| ... | @@ -514,13 +515,21 @@ const SwitchMessage = struct { | ... | @@ -514,13 +515,21 @@ const SwitchMessage = struct { |
| 514 | .register_awaiter => |awaiter| { | 515 | .register_awaiter => |awaiter| { |
| 515 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); | 516 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| 516 | assert(prev_fiber.queue_next == null); | 517 | assert(prev_fiber.queue_next == null); |
| 517 | if (@atomicRmw( | 518 | if (@atomicRmw(?*Fiber, awaiter, .Xchg, prev_fiber, .acq_rel) == Fiber.finished) |
| 518 | ?*Fiber, | 519 | el.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber }); |
| 519 | awaiter, | 520 | }, |
| 520 | .Xchg, | 521 | .register_select => |futures| { |
| 521 | prev_fiber, | 522 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| 522 | .acq_rel, | 523 | assert(prev_fiber.queue_next == null); |
| 523 | ) == Fiber.finished) el.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber }); | 524 | for (futures) |any_future| { |
| | 525 | const future_fiber: *Fiber = @alignCast(@ptrCast(any_future)); |
| | 526 | if (@atomicRmw(?*Fiber, &future_fiber.awaiter, .Xchg, prev_fiber, .acq_rel) == Fiber.finished) { |
| | 527 | const closure: *AsyncClosure = .fromFiber(future_fiber); |
| | 528 | if (!@atomicRmw(bool, &closure.already_awaited, .Xchg, true, .seq_cst)) { |
| | 529 | el.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber }); |
| | 530 | } |
| | 531 | } |
| | 532 | } |
| 524 | }, | 533 | }, |
| 525 | .mutex_lock => |mutex_lock| { | 534 | .mutex_lock => |mutex_lock| { |
| 526 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); | 535 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); |
| ... | @@ -661,6 +670,7 @@ const AsyncClosure = struct { | ... | @@ -661,6 +670,7 @@ const AsyncClosure = struct { |
| 661 | fiber: *Fiber, | 670 | fiber: *Fiber, |
| 662 | start: *const fn (context: *const anyopaque, result: *anyopaque) void, | 671 | start: *const fn (context: *const anyopaque, result: *anyopaque) void, |
| 663 | result_align: Alignment, | 672 | result_align: Alignment, |
| | 673 | already_awaited: bool, |
| 664 | | 674 | |
| 665 | fn contextPointer(closure: *AsyncClosure) [*]align(Fiber.max_context_align.toByteUnits()) u8 { | 675 | fn contextPointer(closure: *AsyncClosure) [*]align(Fiber.max_context_align.toByteUnits()) u8 { |
| 666 | return @alignCast(@as([*]u8, @ptrCast(closure)) + @sizeOf(AsyncClosure)); | 676 | return @alignCast(@as([*]u8, @ptrCast(closure)) + @sizeOf(AsyncClosure)); |
| ... | @@ -668,12 +678,24 @@ const AsyncClosure = struct { | ... | @@ -668,12 +678,24 @@ const AsyncClosure = struct { |
| 668 | | 678 | |
| 669 | fn call(closure: *AsyncClosure, message: *const SwitchMessage) callconv(.withStackAlign(.c, @alignOf(AsyncClosure))) noreturn { | 679 | fn call(closure: *AsyncClosure, message: *const SwitchMessage) callconv(.withStackAlign(.c, @alignOf(AsyncClosure))) noreturn { |
| 670 | message.handle(closure.event_loop); | 680 | message.handle(closure.event_loop); |
| 671 | std.log.debug("{*} performing async", .{closure.fiber}); | 681 | const fiber = closure.fiber; |
| 672 | closure.start(closure.contextPointer(), closure.fiber.resultBytes(closure.result_align)); | 682 | std.log.debug("{*} performing async", .{fiber}); |
| 673 | const awaiter = @atomicRmw(?*Fiber, &closure.fiber.awaiter, .Xchg, Fiber.finished, .acq_rel); | 683 | closure.start(closure.contextPointer(), fiber.resultBytes(closure.result_align)); |
| 674 | closure.event_loop.yield(awaiter, .nothing); | 684 | const awaiter = @atomicRmw(?*Fiber, &fiber.awaiter, .Xchg, Fiber.finished, .acq_rel); |
| | 685 | const ready_awaiter = r: { |
| | 686 | const a = awaiter orelse break :r null; |
| | 687 | if (@atomicRmw(bool, &closure.already_awaited, .Xchg, true, .acq_rel)) break :r null; |
| | 688 | break :r a; |
| | 689 | }; |
| | 690 | closure.event_loop.yield(ready_awaiter, .nothing); |
| 675 | unreachable; // switched to dead fiber | 691 | unreachable; // switched to dead fiber |
| 676 | } | 692 | } |
| | 693 | |
| | 694 | fn fromFiber(fiber: *Fiber) *AsyncClosure { |
| | 695 | return @ptrFromInt(Fiber.max_context_align.max(.of(AsyncClosure)).backward( |
| | 696 | @intFromPtr(fiber.allocatedEnd()) - Fiber.max_context_size, |
| | 697 | ) - @sizeOf(AsyncClosure)); |
| | 698 | } |
| 677 | }; | 699 | }; |
| 678 | | 700 | |
| 679 | fn @"async"( | 701 | fn @"async"( |
| ... | @@ -696,9 +718,7 @@ fn @"async"( | ... | @@ -696,9 +718,7 @@ fn @"async"( |
| 696 | }; | 718 | }; |
| 697 | std.log.debug("allocated {*}", .{fiber}); | 719 | std.log.debug("allocated {*}", .{fiber}); |
| 698 | | 720 | |
| 699 | const closure: *AsyncClosure = @ptrFromInt(Fiber.max_context_align.max(.of(AsyncClosure)).backward( | 721 | const closure: *AsyncClosure = .fromFiber(fiber); |
| 700 | @intFromPtr(fiber.allocatedEnd()) - Fiber.max_context_size, | | |
| 701 | ) - @sizeOf(AsyncClosure)); | | |
| 702 | const stack_end: [*]usize = @alignCast(@ptrCast(closure)); | 722 | const stack_end: [*]usize = @alignCast(@ptrCast(closure)); |
| 703 | (stack_end - 1)[0..1].* = .{@intFromPtr(&AsyncClosure.call)}; | 723 | (stack_end - 1)[0..1].* = .{@intFromPtr(&AsyncClosure.call)}; |
| 704 | fiber.* = .{ | 724 | fiber.* = .{ |
| ... | @@ -721,6 +741,7 @@ fn @"async"( | ... | @@ -721,6 +741,7 @@ fn @"async"( |
| 721 | .fiber = fiber, | 741 | .fiber = fiber, |
| 722 | .start = start, | 742 | .start = start, |
| 723 | .result_align = result_alignment, | 743 | .result_align = result_alignment, |
| | 744 | .already_awaited = false, |
| 724 | }; | 745 | }; |
| 725 | @memcpy(closure.contextPointer(), context); | 746 | @memcpy(closure.contextPointer(), context); |
| 726 | | 747 | |
| ... | @@ -728,13 +749,6 @@ fn @"async"( | ... | @@ -728,13 +749,6 @@ fn @"async"( |
| 728 | return @ptrCast(fiber); | 749 | return @ptrCast(fiber); |
| 729 | } | 750 | } |
| 730 | | 751 | |
| 731 | fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { | | |
| 732 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); | | |
| 733 | _ = el; | | |
| 734 | _ = futures; | | |
| 735 | @panic("TODO"); | | |
| 736 | } | | |
| 737 | | | |
| 738 | const DetachedClosure = struct { | 752 | const DetachedClosure = struct { |
| 739 | event_loop: *EventLoop, | 753 | event_loop: *EventLoop, |
| 740 | fiber: *Fiber, | 754 | fiber: *Fiber, |
| ... | @@ -836,6 +850,42 @@ fn @"await"( | ... | @@ -836,6 +850,42 @@ fn @"await"( |
| 836 | event_loop.recycle(future_fiber); | 850 | event_loop.recycle(future_fiber); |
| 837 | } | 851 | } |
| 838 | | 852 | |
| | 853 | fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { |
| | 854 | const el: *EventLoop = @alignCast(@ptrCast(userdata)); |
| | 855 | |
| | 856 | // Optimization to avoid the yield below. |
| | 857 | for (futures, 0..) |any_future, i| { |
| | 858 | const future_fiber: *Fiber = @alignCast(@ptrCast(any_future)); |
| | 859 | if (@atomicLoad(?*Fiber, &future_fiber.awaiter, .acquire) == Fiber.finished) |
| | 860 | return i; |
| | 861 | } |
| | 862 | |
| | 863 | el.yield(null, .{ .register_select = futures }); |
| | 864 | |
| | 865 | std.log.debug("back from select yield", .{}); |
| | 866 | |
| | 867 | const my_thread: *Thread = .current(); |
| | 868 | const my_fiber = my_thread.currentFiber(); |
| | 869 | var result: ?usize = null; |
| | 870 | |
| | 871 | for (futures, 0..) |any_future, i| { |
| | 872 | const future_fiber: *Fiber = @alignCast(@ptrCast(any_future)); |
| | 873 | if (@cmpxchgStrong(?*Fiber, &future_fiber.awaiter, my_fiber, null, .seq_cst, .seq_cst)) |awaiter| { |
| | 874 | if (awaiter == Fiber.finished) { |
| | 875 | if (result == null) result = i; |
| | 876 | } else if (awaiter) |a| { |
| | 877 | const closure: *AsyncClosure = .fromFiber(a); |
| | 878 | closure.already_awaited = false; |
| | 879 | } |
| | 880 | } else { |
| | 881 | const closure: *AsyncClosure = .fromFiber(my_fiber); |
| | 882 | closure.already_awaited = false; |
| | 883 | } |
| | 884 | } |
| | 885 | |
| | 886 | return result.?; |
| | 887 | } |
| | 888 | |
| 839 | fn cancel( | 889 | fn cancel( |
| 840 | userdata: ?*anyopaque, | 890 | userdata: ?*anyopaque, |
| 841 | any_future: *std.Io.AnyFuture, | 891 | any_future: *std.Io.AnyFuture, |