Commit 7f7e2d608a

Andrew Kelley <andrew@ziglang.org>
2020-10-05 23:37:23
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 parent 939b486
Changed files (1)
lib
std
event
lib/std/event/loop.zig
@@ -647,6 +647,31 @@ pub const Loop = struct {
         }
     }
 
+    /// Runs the provided function asynchronously. The function's frame is allocated
+    /// with `allocator` and freed when the function returns.
+    /// `func` must return void and it can be an async function.
+    /// Yields to the event loop, running the function on the next tick.
+    pub fn runDetached(self: *Loop, alloc: *mem.Allocator, comptime func: anytype, args: anytype) error{OutOfMemory}!void {
+        if (!std.io.is_async) @compileError("Can't use runDetached in non-async mode!");
+        if (@TypeOf(@call(.{}, func, args)) != void) {
+            @compileError("`func` must not have a return value");
+        }
+
+        const Wrapper = struct {
+            const Args = @TypeOf(args);
+            fn run(func_args: Args, loop: *Loop, allocator: *mem.Allocator) void {
+                loop.yield();
+                const result = @call(.{}, func, func_args);
+                suspend {
+                    allocator.destroy(@frame());
+                }
+            }
+        };
+
+        var run_frame = try alloc.create(@Frame(Wrapper.run));
+        run_frame.* = async Wrapper.run(args, self, alloc);
+    }
+
     /// Yielding lets the event loop run, starting any unstarted async operations.
     /// Note that async operations automatically start when a function yields for any other reason,
     /// 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 {
     testing.expect(value == 1234);
     did_it.* = true;
 }
+
+var testRunDetachedData: usize = 0;
+test "std.event.Loop - runDetached" {
+    // https://github.com/ziglang/zig/issues/1908
+    if (builtin.single_threaded) return error.SkipZigTest;
+    if (!std.io.is_async) return error.SkipZigTest;
+    if (true) {
+        // https://github.com/ziglang/zig/issues/4922
+        return error.SkipZigTest;
+    }
+
+    var loop: Loop = undefined;
+    try loop.initMultiThreaded();
+    defer loop.deinit();
+
+    // Schedule the execution, won't actually start until we start the
+    // event loop.
+    try loop.runDetached(std.testing.allocator, testRunDetached, .{});
+
+    // Now we can start the event loop. The function will return only
+    // after all tasks have been completed, allowing us to synchonize
+    // with the previous runDetached.
+    loop.run();
+
+    testing.expect(testRunDetachedData == 1);
+}
+
+fn testRunDetached() void {
+    testRunDetachedData += 1;
+}