| // 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); |