//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();
}