authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-05 14:37:23-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-05 14:41:51-07:00
log7f7e2d608adb81cd00e54fd7fe5e7035a890565f
treeb23fa4dd6a1a65a289e618c7853453f77f720c6e
parent939b4860ef2990ba453842033475a18f14a5b72e

Revert "revert adding std.event.Loop.runDetached"

This reverts commit 70f37679035e64bacfc4807709da37bc02fbb346. After discussion, I can see the value provided here, specifically with avoiding the footgun of defer { suspend { free(@frame()); } }. However the doc comments are updated to explain the semantics directly, rather than basing them on the behavior of another programming language.

1 files changed, 55 insertions(+), 0 deletions(-)

lib/std/event/loop.zig+55
...@@ -647,6 +647,31 @@ pub const Loop = struct {...@@ -647,6 +647,31 @@ pub const Loop = struct {
647 }647 }
648 }648 }
649649
650 /// Runs the provided function asynchronously. The function's frame is allocated
651 /// with `allocator` and freed when the function returns.
652 /// `func` must return void and it can be an async function.
653 /// Yields to the event loop, running the function on the next tick.
654 pub fn runDetached(self: *Loop, alloc: *mem.Allocator, comptime func: anytype, args: anytype) error{OutOfMemory}!void {
655 if (!std.io.is_async) @compileError("Can't use runDetached in non-async mode!");
656 if (@TypeOf(@call(.{}, func, args)) != void) {
657 @compileError("`func` must not have a return value");
658 }
659
660 const Wrapper = struct {
661 const Args = @TypeOf(args);
662 fn run(func_args: Args, loop: *Loop, allocator: *mem.Allocator) void {
663 loop.yield();
664 const result = @call(.{}, func, func_args);
665 suspend {
666 allocator.destroy(@frame());
667 }
668 }
669 };
670
671 var run_frame = try alloc.create(@Frame(Wrapper.run));
672 run_frame.* = async Wrapper.run(args, self, alloc);
673 }
674
650 /// Yielding lets the event loop run, starting any unstarted async operations.675 /// Yielding lets the event loop run, starting any unstarted async operations.
651 /// Note that async operations automatically start when a function yields for any other reason,676 /// Note that async operations automatically start when a function yields for any other reason,
652 /// for example, when async I/O is performed. This function is intended to be used only when677 /// for example, when async I/O is performed. This function is intended to be used only when
...@@ -1493,3 +1518,33 @@ fn testEventLoop2(h: anyframe->i32, did_it: *bool) void {...@@ -1493,3 +1518,33 @@ fn testEventLoop2(h: anyframe->i32, did_it: *bool) void {
1493 testing.expect(value == 1234);1518 testing.expect(value == 1234);
1494 did_it.* = true;1519 did_it.* = true;
1495}1520}
1521
1522var testRunDetachedData: usize = 0;
1523test "std.event.Loop - runDetached" {
1524 // https://github.com/ziglang/zig/issues/1908
1525 if (builtin.single_threaded) return error.SkipZigTest;
1526 if (!std.io.is_async) return error.SkipZigTest;
1527 if (true) {
1528 // https://github.com/ziglang/zig/issues/4922
1529 return error.SkipZigTest;
1530 }
1531
1532 var loop: Loop = undefined;
1533 try loop.initMultiThreaded();
1534 defer loop.deinit();
1535
1536 // Schedule the execution, won't actually start until we start the
1537 // event loop.
1538 try loop.runDetached(std.testing.allocator, testRunDetached, .{});
1539
1540 // Now we can start the event loop. The function will return only
1541 // after all tasks have been completed, allowing us to synchonize
1542 // with the previous runDetached.
1543 loop.run();
1544
1545 testing.expect(testRunDetachedData == 1);
1546}
1547
1548fn testRunDetached() void {
1549 testRunDetachedData += 1;
1550}