In Zig (currently using 0.7.1), suppose for some reason you don't have any good way to design code which always has exactly one resume
for every suspend
. Is there any supported way to detect at runtime that a given frame has or has not been executed to completion?
// overly simplistic example designed to illustrate the problem
pub fn main() void {
var frame = async amain(2);
resume frame;
resume frame;
// panic/UB -- resume async function which already returned
//
// we would prefer to have some kind of check which could allow
// us to detect at runtime that the following resumption is illegal
resume frame;
}
fn amain(ubound: i32) void {
var i: i32 = 0;
while (i < ubound) : (i += 1) {
suspend;
}
}
If I log the raw bytes of those frame structs (which as far as I can tell are opaque and don't support field access? I'm a little new to Zig), then it seems pretty obvious that a portion of the frame is dedicated to marking whether it has returned:
[70, 3a, 23, 00, 00, 00, 00, 00, 02, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 02, 00, 00, 00, 00, 00, 00, 00]
[70, 3a, 23, 00, 00, 00, 00, 00, 02, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 02, 00, 00, 00, 01, 00, 00, 00]
[70, 3a, 23, 00, 00, 00, 00, 00, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, ff, 02, 00, 00, 00, 02, 00, 00, 00]
Blindly reading that data seems a little reckless however, and I'd feel a lot more comfortable if I knew the struct layout were guaranteed or that there were some builtin way to uncover that information.
Edit::
In particular, the problem that I'm trying to solve is that when designing an event loop (e.g. when interfacing between JS and Zig/WASM) you seem to have to marry the implementation of an async function to the API of the event loop itself.
The built-in event loop, for example, has a yield()
function precisely so that it can do the necessary bookkeeping to ensure that code has one resume per suspend, but as far as I can tell that's an unnecessary restriction since whether a frame has returned seems to be stored and readily accessible.
It's certainly possible that I'm misunderstanding the purpose of Zig's async/await, but I don't see any fundamental reason why a general purpose event loop capable of handling any async function (not just those adhering to a particular loop API) couldn't be written, but I'm struggling to see how that's possible without a little more runtime introspection than the docs indicate is available.
As of April 2022 there's an open proposal for implementing this feature in the language. A workaround till then might still be manually reading the async frame header.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With