blob: 418228cb446d9c4012d2a859af2ec1d994137a62 [file] [log] [blame]
// 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);