Last active
October 1, 2019 10:34
-
-
Save ikskuh/e732b1a74f60dc0cdbc0e2f0cf8898f6 to your computer and use it in GitHub Desktop.
Abusing async/await to create fake threads
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
/// stores some state for each coroutine | |
/// as well as a pointer to the frame to be resumed | |
const CoroutineState = struct { | |
pub const Self = @This(); | |
frame: anyframe = undefined, | |
quit: bool = false, | |
active: bool = false, | |
/// must be called from within the coroutine | |
/// as soon as the coroutine will exit. | |
/// this is best done by using `defer coro.exit();`. | |
pub fn exit(self: *Self) void { | |
self.active = false; | |
} | |
/// yields the coroutine. | |
/// will return an error if the coroutine has been quit. | |
fn yield(coro: *Self) !void { | |
suspend { | |
coro.frame = @frame(); | |
} | |
if (coro.quit) | |
return error.Cancelled; | |
} | |
}; | |
var coroutines = [_]CoroutineState{CoroutineState{}} ** 3; | |
/// our coroutine | |
fn loop(coro: *CoroutineState, prefix: []const u8, limit: i32) !void { | |
defer coro.exit(); // must be called to recognize that the coroutine was stopped | |
std.debug.warn("{}: startup\n", prefix); | |
defer std.debug.warn("{}: shutdown\n", prefix); | |
var repetition: i32 = 0; | |
while (repetition < limit) : (repetition += 1) { | |
std.debug.warn("{}: loop {}\n", prefix, repetition); | |
try coro.yield(); // yield may throw error.Cancelled | |
} | |
} | |
/// starts a new coroutine with the given parameters | |
fn start_new(prefix: []const u8, limit: i32) !@Frame(loop) { | |
for (coroutines) |*coro| { | |
if (coro.active) | |
continue; | |
coro.active = true; | |
coro.quit = false; | |
coro.frame = undefined; | |
return async loop(coro, prefix, limit); | |
} | |
return error.OutOfMemory; | |
} | |
pub fn main() !void { | |
// this uses result location semantics to | |
// store our coroutine frames in the main | |
// function. in a proper implementation, | |
// it should use an allocator | |
_ = try start_new("o", 5); | |
_ = try start_new("x", 10); | |
_ = try start_new("~", 20); | |
// schedule our coroutines here 15 times | |
var rep: u32 = 0; | |
while (rep < 15) : (rep += 1) { | |
var any_alive = false; | |
for (coroutines) |coro| { | |
// resume all alive coroutines | |
if (!coro.active) | |
continue; | |
any_alive = true; | |
resume coro.frame; | |
} | |
if (!any_alive) | |
break; | |
} | |
// kill all coroutines that are still alive | |
for (coroutines) |*coro, index| { | |
if (coro.active) { | |
coro.quit = true; | |
resume coro.frame; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
o: startup | |
o: loop 0 | |
x: startup | |
x: loop 0 | |
~: startup | |
~: loop 0 | |
o: loop 1 | |
x: loop 1 | |
~: loop 1 | |
o: loop 2 | |
x: loop 2 | |
~: loop 2 | |
o: loop 3 | |
x: loop 3 | |
~: loop 3 | |
o: loop 4 | |
x: loop 4 | |
~: loop 4 | |
o: shutdown | |
x: loop 5 | |
~: loop 5 | |
x: loop 6 | |
~: loop 6 | |
x: loop 7 | |
~: loop 7 | |
x: loop 8 | |
~: loop 8 | |
x: loop 9 | |
~: loop 9 | |
x: shutdown | |
~: loop 10 | |
~: loop 11 | |
~: loop 12 | |
~: loop 13 | |
~: loop 14 | |
~: loop 15 | |
~: shutdown |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment