Haiku wait_for_objects(): Experimental Multiplexing Across Kernel Objects
Monitor Haiku file descriptors, semaphores, ports, and threads with experimental wait_for_objects_etc(), while handling mutable masks and stale IDs.
Haiku exposes wait_for_objects() and wait_for_objects_etc() as a kernel-facing way to wait on a heterogeneous set of objects: file descriptors, semaphores, ports, and threads. The public header marks this API experimental. Treat that warning as a compatibility boundary, not a footnote: verify the target Haiku revision and headers, and do not make a portable POSIX program depend on this interface.
The API accepts an array of object_wait_info records. Each record contains an object ID, a type such as B_OBJECT_TYPE_FD or B_OBJECT_TYPE_PORT, and an event mask. The mask has a dual role: on entry it is the set of events requested; on return it is replaced with the events observed for that entry. Reusing the returned array as the next request without restoring the desired masks can silently change what the next wait watches.
Rebuild the request set on every wait
The following is a deliberately small C++ sketch. It combines a readable descriptor, an acquirable semaphore, a readable port, and a thread whose termination invalidates its ID. The owned active list retains the requested masks; each wait receives a fresh copy because the API replaces those masks with observed events:
std::vector<object_wait_info> active = {
{ fd, B_OBJECT_TYPE_FD, B_EVENT_READ },
{ sem, B_OBJECT_TYPE_SEMAPHORE, B_EVENT_ACQUIRE_SEMAPHORE },
{ port, B_OBJECT_TYPE_PORT, B_EVENT_READ },
{ thread, B_OBJECT_TYPE_THREAD, B_EVENT_INVALID }
};
while (running && !active.empty()) {
std::vector<object_wait_info> watched = active;
ssize_t ready = wait_for_objects_etc(watched.data(),
static_cast<int>(watched.size()),
B_RELATIVE_TIMEOUT, 500000);
if (ready < B_OK) {
if (ready == B_TIMED_OUT)
continue;
return (status_t)ready;
}
for (size_t i = 0; i < watched.size();) {
if (watched[i].events & B_EVENT_INVALID) {
active.erase(active.begin() + i);
watched.erase(watched.begin() + i);
continue;
}
/* Dispatch the reported event using the matching object API. */
i++;
}
}
This sketch assumes Haiku’s kernel API declarations (for example, <OS.h>), surrounding object IDs and dispatch code, and C++11 or later for initializer-list construction and vector::data(). Production code should also define object-specific ownership cleanup. Removing an invalid ID from both the owned list and this result copy prevents the next iteration from re-adding it.
The 500,000 value is an illustrative relative timeout in microseconds, not a recommended service deadline. A timeout, interruption, or nonblocking result is returned as a negative status; on those paths the implementation clears the returned event masks. On success the return value is the number of array entries that reported one or more events, not the number of bits set across all masks. The array still needs to be examined entry by entry.
An event is a readiness notification, not a reservation or a consumed resource. After a semaphore-ready event, another thread could acquire the semaphore first; after a port-readable event, another reader could consume the message. Perform the corresponding operation with a bounded or nonblocking policy and handle its result. Haiku’s own test follows this pattern by attempting operations with zero-timeout calls after examining the reported events.
Account for events that are reported automatically
The header states that B_EVENT_INVALID, B_EVENT_ERROR, and B_EVENT_DISCONNECTED do not need to be requested; they are reported when they occur. Invalid means the ID is not valid or is no longer valid, for example because an object was closed or deleted. Handle these conditions before dispatching a normal read, write, or acquire operation, and remove stale identifiers from future arrays. Reusing an ID after the object has gone away is not a recovery strategy.
Event meanings depend on the object type. For descriptors and ports, read and write bits report readiness; the semaphore event is B_EVENT_ACQUIRE_SEMAPHORE; the kernel test uses B_EVENT_INVALID for thread lifecycle observation. Do not interpret a bit in isolation from the entry’s type. Likewise, do not assume that all file descriptors support every priority event merely because the constants appear in the common header.
Because the input masks are mutated, keep an immutable template or initialize a fresh array from the desired object IDs, types, and masks before each call. The official test copies its initial array on every iteration. If the object set changes during an event handler, build the next wait set from the current owned state rather than patching stale results in place.
Keep portability and lifecycle explicit
This interface can reduce the need to coordinate separate wait loops when a Haiku component genuinely needs to monitor kernel objects of several kinds. It does not remove each object’s ownership rules: the consumer still owns descriptor close, semaphore deletion/acquisition, port reads, and thread joins. Arrange shutdown so the wait loop is woken, every pending object is retired once, and a stale ID cannot survive into the next array.
For a POSIX-only descriptor set, use the portable facility required by the target (poll, select, or a platform event queue) rather than importing Haiku’s experimental object types. If a library wraps wait_for_objects_etc(), isolate it behind a Haiku-specific adapter and make the experimental dependency visible in the build and support policy. Pin the Haiku revision used for validation and compile-test against the actual target headers; the public warning means behavior and availability may change.
Test one object of each supported kind, multiple simultaneous ready entries, a timeout, interruption, deletion while waiting, and an invalid ID at entry. Verify the result count independently from the bit masks, then force a competing consumer to demonstrate that readiness does not guarantee the subsequent operation will succeed. Those checks make the API useful without mistaking notification for ownership.
Related:
- Haiku Kernel Ports: Bounded Message Queues and IPC Lifecycle
- Haiku Kernel Semaphores: Counting, Timeouts, and Safe Shutdown
Sources: