Waitany
| Number | 0x17 |
|---|---|
| Signature | (handles*, count, timeout_ms, result*) -> 0 or -err |
| Group | messaging |
| Since | 1.0 |
| Blocking | conditional |
Wait on several sources at once. It is the multiplexing primitive behind every event-loop
server. Blocks until any handle in the set has a message or signal ready, then fills
result describing which one fired and what it carried.
Unlike MsgRecv (one port) or NtfnWait (one notification), Waitany lets one thread serve
many clients and react to notifications from a single blocking point, which is why a
server needs only one thread, not one per client.
The result is delivered through a caller-provided struct rather than registers, because it
must say which handle fired and what kind of event it was. The wrapper sets size for
version negotiation; the kernel fills the rest:
| Field | Meaning |
|---|---|
size |
Set by the wrapper to sizeof(WaitanyResult). |
matched_index |
Index into your handles array of the source that fired; WAITANY_NO_MATCH on timeout. |
kind |
Which kind of event fired: see below. |
source, r1, r2, r3 |
Payload, reinterpreted per kind. |
marker |
(since 1.1) The marker on the handle the sender used, if Stamped; MARKER_NONE otherwise. Set for SEND and CALL, always MARKER_NONE for NTFN and TIMEOUT. |
The four payload slots mean different things depending on kind:
kind |
source |
r1 |
r2, r3 |
|---|---|---|---|
WAITANY_KIND_SEND |
sender PID deprecated, see below | payload / lmsg length | payload words |
WAITANY_KIND_CALL |
reply handle | sender PID deprecated, see below | payload / lmsg length |
WAITANY_KIND_NTFN |
0 | notification bits | — |
WAITANY_KIND_TIMEOUT |
— | — | — |
As of zuzu v1.1, the sender PID field has been deprecated and will be removed by zuzu v2.0 Prowl. Use the marker field to identify the sender.
So a server’s dispatch loop switches on result.kind: a CALL gives you a reply handle in
source to answer with MsgReply/MsgLreply; a SEND is fire-and-forget with the sender in
source; an NTFN carries its signalled bits in r1; a TIMEOUT means the deadline
expired with matched_index set to WAITANY_NO_MATCH.
A single port can be handed out under several handles, each Stamped with a different
marker. All of them wake the same Waitany set through the one underlying port, but
result.marker tells the server which stamped handle the sender actually used, so one
port and one wait loop can demultiplex many logical clients without a handle per client.
Two subtleties in the timeout behaviour:
- A poll (
TIMEOUT_POLL) that finds nothing returnsERR_TIMEOUT. - A finite deadline that expires returns
0withkind == WAITANY_KIND_TIMEOUTwhich is a success whosematched_indexisWAITANY_NO_MATCH.
A correct loop therefore checks both: a negative return for the poll case, and
kind == WAITANY_KIND_TIMEOUT on a zero return for the deadline case.
At most WAITANY_MAX_HANDLES handles can be waited on in one call; a larger count is
rejected with ERR_BADARG.
Pitfalls
kind must be checked before reading any payload slot, because the slots are reinterpreted, reading source as a sender PID when the kind is CALL gives you a reply handle instead, and vice versa. This is the single most common Waitany bug.
Arguments
| Register | Name | Description |
|---|---|---|
r0 | handles | Array of handles (ports and/or notifications) to wait on |
r1 | count | Number of handles in the array |
r2 | timeout_ms | `TIMEOUT_POLL` to poll, `TIMEOUT_INFINITE` to block, otherwise a deadline in ms |
r3 | result | Pointer to a `WaitanyResult` the kernel fills |
Returns
0 on success; the fired source is described in `*result`.
Errors
| Code | Condition |
|---|---|
ERR_BADARG | count is 0 or exceeds WAITANY_MAX_HANDLES, or result->size is smaller than the kernel's struct |
ERR_BADPTR | handles or result pointer is invalid or unwritable |
ERR_DEAD | A waited port or notification was destroyed while blocked |
ERR_TIMEOUT | TIMEOUT_POLL and nothing was ready |