Waitany

Number0x17
Signature(handles*, count, timeout_ms, result*) -> 0 or -err
Groupmessaging
Since1.0
Blockingconditional

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 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

RegisterNameDescription
r0handlesArray of handles (ports and/or notifications) to wait on
r1countNumber of handles in the array
r2timeout_ms`TIMEOUT_POLL` to poll, `TIMEOUT_INFINITE` to block, otherwise a deadline in ms
r3resultPointer to a `WaitanyResult` the kernel fills

Returns

0 on success; the fired source is described in `*result`.

Errors

CodeCondition
ERR_BADARGcount is 0 or exceeds WAITANY_MAX_HANDLES, or result->size is smaller than the kernel's struct
ERR_BADPTRhandles or result pointer is invalid or unwritable
ERR_DEADA waited port or notification was destroyed while blocked
ERR_TIMEOUTTIMEOUT_POLL and nothing was ready

See also