|
//onReady takes a callback and a frame pointer, |
|
//and then at some point passes the frame pointer to the callback. |
|
//the frame pointer is typed as anyopaque (same as c void pointer) |
|
//instead of any frame because of compiler errors in get |
|
extern fn onReady( |
|
cb: fn ( frame: *anyopaque ) callconv(.C) void, |
|
frame: *anyopaque |
|
) void; |
|
|
|
//copies memory from host into pointer, up to len |
|
extern fn read(ptr: [*]const u8, len:usize) usize; |
|
//writes len bytes from ptr to stdout |
|
extern fn print(ptr: [*]const u8, len:usize) void; |
|
|
|
fn get (slice: []u8) usize { |
|
//put a suspend here, otherwise async get() |
|
//will run to the end then return frame at return value. |
|
//we don't want to do that because memory to read isn't ready yet. |
|
suspend { |
|
//because we pass a pointer to our own frame to an external function |
|
//that parameter must be typed *anyopaque. if it's typed anyframe |
|
//there are compiler errors that "@Frame(get) not analyzed yet" |
|
onReady(cb, @frame()); |
|
} |
|
return read(slice.ptr, slice.len); |
|
} |
|
|
|
fn cb (frame: *anyopaque) callconv(.C) void { |
|
//defer allocator.destroy(frame); |
|
//cast frame:*anyopaque to frame:*anyframe so we can resume it. |
|
//this requires also casting the alignment. |
|
resume @ptrCast(anyframe, @alignCast(4, frame)); |
|
} |
|
|
|
//internal _init function, this can have any sort of async pattern. |
|
fn _init () void { |
|
var array1:[1]u8 = [_]u8{0}; |
|
//note: because of zig's uncoloured async, this just looks like a normal loop |
|
while (0 != get(array1[0..array1.len])) { |
|
print(&array1, 1); |
|
} |
|
} |
|
|
|
//this is the essential trick, store the _init frame in global variable. |
|
var init_frame :@Frame(_init) = undefined; |
|
//the exported function can't be async |
|
//but we can call another function with async keyword |
|
//but if we did that with a local variable the memory wouldn't be alive |
|
//after init() returns |
|
//so, we make it a global. then, call async _init |
|
//and have any kind of async pattern in there. |
|
|
|
//(note, this does mean that init() should only be called exactly once, like main()) |
|
export fn init () void { |
|
init_frame = async _init(); |
|
} |
This knowledge is quite beneficial. Right word hurdle when I needed it the most