Closed guybedford closed 10 months ago
Here is my interpretation of the state machine, please let me know if this sounds correct:
Listen states:
would-block
is thrown.Is it valid to ever implement accept as blocking? Or should the subscribe pollable always be a requirement for a fully async implementation (the initial implementation in JCO was naively blocking).
Just to be sure; did you see the Asynchronous APIs section in the readme?
LISTEN_FINISH: Listen finish is called. unresolved pollable
It might be the terminology messing with me, but there is no distinct LISTEN_FINISH phase that needs to be awaited. After LISTEN_START (and also LISTEN_READY in your case), there is only "LISTENING". And only when a socket is Listening, you can call accept
.
Is it valid to ever implement accept as blocking?
No. If accept
were to block, guest programs would run into problems when for example they'd like to accept connections on multiple ports.
Is it expected that accept can be subscribed to?
Yes :) And just to be clear; accept
itself can't be "subscribed" to. Poll-ability is a property of the socket itself, not any specific operation. My mental model of the situation is more like this:
interface Pollable { // NOT a resource on its own
ready(): boolean;
// ...
}
class TcpSocket implements Pollable {
#activeOperation: Promise | null;
startConnect(...) {
if (#activeOperation !== null) { err }
#activeOperation = nativeSocket.connect(...);
}
finishConnect(...) {
if (#activeOperation === null || /*#activeOperation !== a connect operation */) { err }
if (#activeOperation.isPending) {
return WouldBlock;
}
#activeOperation = null;
}
ready() {
return #activeOperation === null || #activeOperation.isPending == false;
}
}
Hope that helps.
Thanks for the clarifications here again, the example helps a lot. I've had to onboard into the implementation details at a later stage hence my beginner questions, but I hope that the feedback does at least help to understand implementation specification knowledge gaps from the perspective of a novice.
Again, I'm glad I asked the basic question - since bind
is a synchronous operation we implemented it in the finishBind
method. But it seems like connect
should be implemented in the startConnect
method not in finishConnect
as we had done. I will make this change in JCO as well.
I do think there needs to be an explicit LISTEN_FINISH
at least from the perspective of the pollable state machine before LISTENING
, because the pollable must be unresolved in LISTENING
not resolved. Otherwise how could the pollable transition back into an unresolved state for accepts?
Edit: The short version here being that calling finishConnect
resets the pollable to an unready state, but does the same apply to other calls like finishBind
? Is the pollable in a ready state after that call? This is the distinction I'm seeking clarification on in the initial issue.
From discussions offline I wanted to summarize the answer here:
finishListen
, the pollable is in a resolved state. Subscribing will not trigger any transitions at all.accept
will return would-block
after which the pollable will revert back into an unresolved state.accept
call can then be used to subscribe to the asynchronous accept.accept
.After discussing this further with @elliottt and also looking at the Wasmtime implementation, we came to the conclusion that what I described in https://github.com/WebAssembly/wasi-sockets/issues/91#issuecomment-1887625192 might not actually be what is happening in Wasmtime. Instead it appears like Wasmtime is:
Still not 100% sure either way, I'm just trying to get this one completely clear for implementation work.
Is it expected that accept can be subscribed to?
If so I'd value some clarification on what the state of the subscribe pollable should be at the end of
finishListen
? Resolved or unresolved?