'In Zig, how do you tell if a frame represents a fully executed function?

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.

zig


Solution 1:[1]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Hans Musgrave