| // Copyright Microsoft and CHERIoT Contributors. | |
| // SPDX-License-Identifier: MIT | |
| #pragma once | |
| /** | |
| * @file multiwaiter.h | |
| * | |
| * This file provides interfaces to the multi-waiter system. A multi-waiter | |
| * object (or 'multiwaiter') is an object that allows a calling thread to | |
| * suspend execution until one of a set of events has occurred. | |
| * | |
| * The CHERIoT multiwaiter system is designed to avoid allocation (or | |
| * interaction with the allocator) on the fast path. The scheduler may not | |
| * capture any of the arguments to the multiwaiter's wait call and expose them | |
| * to other threads unless they are heap allocated. It must also be robust in | |
| * the presence of malicious code that attempts to concurrently mutate any data | |
| * structures while sleeping. | |
| * | |
| * The multiwaiter object is allocated with space to wait for *n* objects, up | |
| * to a fixed limit. Each wait call provides a set of things to wait on and | |
| * will suspend until either one occurs or a timeout is reached. On return, | |
| * the caller-provided list will be updated. This list can be allocated on the | |
| * stack: the scheduler does not need to hold a copy of it between calls or | |
| * write to it from another thread. | |
| * | |
| * The multiwaiter should be used sparingly. In the worst case, it can add a | |
| * linear-complexity overhead on all wake events. Memory overhead and code | |
| * size have been the key optimisation goals for this design. Unlike systems | |
| * such as `kqueue`, scalability has not been a priority in this design because | |
| * expected number of waited objects is small and so is the number of threads. | |
| */ | |
| #include <compartment.h> | |
| #include <stdlib.h> | |
| #include <timeout.h> | |
| /** | |
| * The kind of event source to wait for in a multiwaiter. | |
| */ | |
| enum EventWaiterKind | |
| { | |
| /// Event source is an event channel. | |
| EventWaiterEventChannel, | |
| /// Event source is a futex. | |
| EventWaiterFutex | |
| }; | |
| enum [[clang::flag_enum]] EventWaiterEventChannelFlags{ | |
| /// Automatically clear the bits we waited on. | |
| EventWaiterEventChannelClearOnExit = (1 << 24), | |
| /// Notify when all bits were set. | |
| EventWaiterEventChannelWaitAll = (1 << 26)}; | |
| /** | |
| * Structure describing a change to the set of managed event sources for an | |
| * event waiter. | |
| */ | |
| struct EventWaiterSource | |
| { | |
| /** | |
| * A pointer to the event source. For a futex, this should be the memory | |
| * address. For other sources, it should be a pointer to an object of the | |
| * corresponding type. | |
| */ | |
| void *eventSource; | |
| /** | |
| * The kind of the event source. This must match the pointer type. | |
| */ | |
| enum EventWaiterKind kind; | |
| /** | |
| * Event-specific configuration. This field is modified during the wait | |
| * call. The interpretation of this depends on `kind`: | |
| * | |
| * - `EventWaiterEventChannel`: The low 24 bits contain the bits to | |
| * monitor, the top bit indicates whether this event is triggered if all | |
| * of the bits are set (true) or some of them (false). On return, this | |
| * contains the bits that have been set during the call. | |
| * - `EventWaiterFutex`: This indicates the value to compare the futex word | |
| * against. If they mismatch, the event fires immediately. | |
| * | |
| * If waiting for a futex, signal the event immediately if the value | |
| * does not match. On return, this is set to 1 if the futex is | |
| * signaled, 0 otherwise. | |
| */ | |
| uint32_t value; | |
| }; | |
| /** | |
| * Opqaue type for multiwaiter objects. Callers will always see this as a | |
| * sealed object. | |
| */ | |
| struct MultiWaiter; | |
| /** | |
| * Create a multiwaiter object. This is a stateful object that can wait on at | |
| * most `maxItems` event sources. | |
| */ | |
| [[cheri::interrupt_state(disabled)]] int __cheri_compartment("sched") | |
| multiwaiter_create(Timeout *timeout, | |
| struct SObjStruct *heapCapability, | |
| struct MultiWaiter **ret, | |
| size_t maxItems); | |
| /** | |
| * Destroy a multiwaiter object. | |
| */ | |
| [[cheri::interrupt_state(disabled)]] int __cheri_compartment("sched") | |
| multiwaiter_delete(struct SObjStruct *heapCapability, struct MultiWaiter *mw); | |
| /** | |
| * Wait for events. The first argument is the multiwaiter to wait on. New | |
| * events can optionally be added by providing an array of `newEventsCount` | |
| * elements as the `newEvents` argument. | |
| * | |
| * Return values: | |
| * | |
| * - On success, this function returns 0. | |
| * - If the arguments are invalid, this function returns -EINVAL. | |
| * - If the timeout is reached without any events being triggered then this | |
| * returns -ETIMEOUT. | |
| */ | |
| [[cheri::interrupt_state(disabled)]] int __cheri_compartment("sched") | |
| multiwaiter_wait(Timeout *timeout, | |
| struct MultiWaiter *waiter, | |
| struct EventWaiterSource *events, | |
| size_t newEventsCount); |