zig.guide icon indicating copy to clipboard operation
zig.guide copied to clipboard

Referencing stack memory and a @frame pointer before suspending

Open jorangreef opened this issue 3 years ago • 2 comments

Hey @Sobeston thanks for ziglearn! It's been a huge help learning Zig.

Just an idea to add to the Async section:

Normally, passing a pointer to a struct allocated on the stack to another function somewhere else is verboten because it's UB (the stack memory disappears and becomes undefined once the function returns).

However, with suspend, it seems like a common pattern is to do exactly this, with a struct allocated on the stack including some data as well as the @frame pointer, and then passing the @ptrToInt of the pointer to this struct somewhere else, e.g. as the user data when submitting an io_uring sqe.

Here are two real world examples from @kprotty and @daurnimator:

https://gist.github.com/kprotty/c01630ae285e8b14cb7a36c454ac2d87#file-uring_async-zig-L101-L102 https://gist.github.com/daurnimator/699320cda828303671a21d15bb4a3753#file-uring-nonblock-fifo-async-zig-L128-L140

I guess this is obvious to people (and really easy!), but for me as a newcomer this was not something I would have thought of because I don't really understand what happens to the async stack after the suspend, and because usually it's not safe to take a pointer to stack memory.

It would be great to see an example of this in the Async section for ziglearn, an explanation that it's safe, and why it works in the case of suspend, showing how suspend keeps the async frame and its stack allocated variables safe... just so people can get going with suspend quicker (assuming they need to) without worrying that they need to do heap allocation.

jorangreef avatar Jan 06 '21 13:01 jorangreef

Normally, passing a pointer to a struct allocated on the stack to another function somewhere else is verboten because it's UB (the stack memory disappears and becomes undefined once the function returns).

Not exactly.

Something like this is entirely valid and normal:

fn foo() void {
   var buf: [100]u8 = undefined;
   bar(&buf);
}

The key thing is not return-ing a pointer to stack memory (or allowing the stack pointer out into the world for longer than after you return).

And suspend is not return.

daurnimator avatar Jan 07 '21 02:01 daurnimator

The key thing is not return-ing a pointer to stack memory (or allowing the stack pointer out into the world for longer than after you return).

Yes, thanks for clarifying, that was the UB I had in mind (the stack memory disappears and becomes undefined once the function returns).

jorangreef avatar Jan 07 '21 05:01 jorangreef