WASI icon indicating copy to clipboard operation
WASI copied to clipboard

Alternative to a "conventional" poll api?

Open lachlansneff opened this issue 6 years ago • 15 comments

Take this issue with a grain-of-salt.

I've noticed that the current (I understand it's a work in progress) wasi api for polling is similar to modern epoll in many ways. Perhaps, implementations that use wasi's polling api (which will presumably be many/most of them) would be simpler if we adopted a simpler, easier to write against api that mirrors that of rust's futures.

I propose that we export a function called register_callback to modules that has the following signature:

(type $t6 (func (param i32 i64 i32) (result i32)))

In rust, that would be:

extern {
    fn register_callback(handle: u32, key: u64, f: extern fn(u64)) -> u32;
}

There would be supporting machinery around it, but essentially, you'd register an event--on a socket for instance--to this handle value, and then register a callback associated with it. The key parameter is passed, unchanged, to the callback when the event occurs.

The callback would, at the wasm level, be the index of an exported function.

I think this would integrate quite directly with rust's Futures and other languages' async internals, though I haven't tried it of course. Plus, it would likely reduce the amount of async machinery a module would have to carry around with it by a very significant amount.

There's an important unanswered question: Would this callback be called on a separate thread by the runtime (sort of like a signal), meaning that it would have to use atomic instructions to interface with the module's memory, or would the module have to relinquish control to the runtime for the callbacks to start being called in a single-threaded manner?

lachlansneff avatar Aug 21 '19 02:08 lachlansneff

I think making it be on another thread limits implementations, and making it on the same thread eventually becomes something like epoll once you generalize it enough, and of course epoll can be expanded to multiple threads. You can also look at something like libuv, which is somewhat similar, although it's tuned for a specific purpose.

devsnek avatar Aug 21 '19 03:08 devsnek

I too wondered about this. @lachlansneff do you know of any previous or further discussion around this online or in the meetings?

jayphelps avatar Oct 01 '19 21:10 jayphelps

@jayphelps, I haven't been involved in the wasm community in quite some time, so I'm not aware of any prior discussions of this.

lachlansneff avatar Oct 02 '19 15:10 lachlansneff

I've worked fairly extensively with callback-based interfaces, and on balance I'd greatly prefer poll-style interfaces. You can easily build callbacks atop poll as a helper library / event-loop library; I don't think the baseline WASI syscalls should inherently use a callback-based interface, though.

joshtriplett avatar Dec 10 '19 10:12 joshtriplett

Something that I've noticed in general: How does WASI even deal with blocking APIs anyway? Isn't WASI meant to work in the browser where you simply can't block the thread at all? So can something like poll even work?

CryZe avatar Dec 10 '19 11:12 CryZe

At this moment in time, all WASI syscalls are blocking. We do want to support async though, and there are many ideas about how to do this.

Callback-style async has some advantages. It puts the event loop in the engine, so that in environments where there's already an event loop (eg. browser main thread, Arduino, and other things), wasm can integrate with it. It also means that wasm can be completely off the stack between events, making it very simple and cheap for engines to switch stacks, suspend/resume, switch JIT tiers, find GC roots, or other things.

Poll-style async also has advantages -- it makes it straightforward to nest async work within synchronous contexts.

Another style is to continue to use a synchronous-style API, and add support for stack switching underneath to allow other code to run while I/O is happening.

It is plausible that WASI overall will support multiple models, or a combination.

sunfishcode avatar Dec 13 '19 20:12 sunfishcode

Async (disregarding whether callback- or poll- or whatever based) was very thoroughly researched by Microsoft some years ago. They've written a whole new OS using this style (i.e. no preemption, but purely async) - see the series http://joeduffyblog.com/2015/11/19/asynchronous-everything/ . But guess what, it's by far not a silver bullet and the disadvantages outweight advantages. So lessons learned is that languages/APIs have to support both sync/preemptive as well as async (and both comparably well).

It is plausible that WASI overall will support multiple models

I'm raising my hand for this.

dumblob avatar Dec 13 '19 22:12 dumblob

It's easy enough to build a callback model atop a pool/select/epoll model. And many applications already have their own event loop, or run atop a language/runtime that has one.

On December 13, 2019 12:39:01 PM PST, Dan Gohman [email protected] wrote:

At this moment in time, all WASI syscalls are blocking. We do want to support async though, and there are many ideas about how to do this.

Callback-style async has some advantages. It puts the event loop in the engine, so that in environments where there's already an event loop (eg. browser main thread, Arduino, and other things), wasm can integrate with it. It also means that wasm can be completely off the stack between events, making it very simple and cheap for engines to switch stacks, suspend/resume, switch JIT tiers, find GC roots, or other things.

Poll-style async also has advantages -- it makes it straightforward to nest async work within synchronous contexts.

Another style is to continue to use a synchronous-style API, and add support for stack switching underneath to allow other code to run while I/O is happening.

It is plausible that WASI overall will support multiple models, or a combination.

joshtriplett avatar Dec 14 '19 16:12 joshtriplett

It's easy enough to build a callback model atop a pool/select/epoll model. And many applications already have their own event loop, or run atop a language/runtime that has one.

But is converse true? (i.e. can you always build a polling system on top of callback system? ). Sometime building polling mechanism on top a callback system can break the semantics. Imagine, for example, a keyboard event callback system that requires the user code to decide it the event is to be handled before the callback returns. Its not possible to stash such an event in queue and handle it later. It has to be handled synchronously.

sbc100 avatar Dec 14 '19 18:12 sbc100

a keyboard event callback system that requires the user code to decide it the event is to be handled before the callback returns

Couldn't the callback just call some user code?

lachlansneff avatar Dec 15 '19 05:12 lachlansneff

a keyboard event callback system that requires the user code to decide it the event is to be handled before the callback returns

Couldn't the callback just call some user code?

In a synchronous event model yes, but not if the user code in question is polling for events.

sbc100 avatar Dec 16 '19 03:12 sbc100

It seems like quite an oversight to not have a callback based api, especially given that the main use case for wasm is the browser, and threads have never really been a part of that. (single threaded processes with message passing, yes)

There are things like shared array buffers, but it's not supported as widely as wasm is.

Also, single threaded processes and message passing is an excellent model (look at erlang) so it's a shame to miss that when everything was already there ready to go.

dominictarr avatar Apr 20 '20 02:04 dominictarr

Why did this get closed? Is this resolved, if so where?

CryZe avatar May 28 '20 22:05 CryZe

@CryZe I'm about to open a new issue that presents a new solution to this problem.

lachlansneff avatar May 28 '20 22:05 lachlansneff

As an update here, WebAssembly CG has recently launched a stack-switching subgroup to pursue language-level approaches here.

sunfishcode avatar Mar 25 '21 19:03 sunfishcode