CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
timeout.h
Go to the documentation of this file.
1// Copyright Microsoft and CHERIoT Contributors.
2// SPDX-License-Identifier: MIT
3
4#pragma once
5/**
6 * @file timeout.h
7 *
8 * This file contains the types used for timeouts across scheduler APIs.
9 */
10
11#include <cdefs.h>
12#include <cheri-builtins.h>
13#include <platform-time.h>
14#include <stdint.h>
15
16/**
17 * Quantity used for measuring time for timeouts. The unit is scheduler ticks.
18 */
19typedef uint32_t Ticks;
20
21/// Value indicating an unbounded timeout for use with `Timeout`.
22static __if_cxx(constexpr) const Ticks UnlimitedTimeout = UINT32_MAX;
23
24/**
25 * Type used for absolute timeouts. These are times against the
26 * platform-specific uptime timer.
27 */
28typedef uint64_t AbsoluteMonotonicTimeout;
29
30/// Timeout value for unlimited blocking.
31static __if_cxx(constexpr) const AbsoluteMonotonicTimeout TimeoutWaitForever =
32 0xffff'ffff'ffff'ffff;
33
34/// Timeout value for not blocking.
35static __if_cxx(constexpr) const AbsoluteMonotonicTimeout TimeoutNoWait = 0;
36
37/**
38 * Structure representing a timeout. This is intended to allow a single
39 * instance to be chained across blocking calls.
40 *
41 * Timeouts *may not be stored in the heap*. Handling timeout structures that
42 * may disappear between sleeping and waking is very complicated and would
43 * impact a lot of fast paths. Instead, most functions that take a timeout
44 * will simply fail if the timeout is on the heap.
45 */
46typedef struct Timeout
47{
48 /**
49 * The time that has elapsed during blocking operations for this timeout
50 * structure. This should be initialised to 0. It may exceed the initial
51 * value of `remaining` if a higher-priority thread preempts the blocking
52 * thread.
53 */
54 Ticks elapsed __if_cxx(= 0);
55 /**
56 * The remaining time. This is clamped at 0 on subtraction. A special
57 * value of `UnlimitedTimeout` can be set to represent an unlimited
58 * timeout.
59 */
61#ifdef __cplusplus
62 /**
63 * Constructor, initialises this structure to allow `time` ticks to
64 * elapse.
65 */
66 Timeout(Ticks time) : remaining(time) {}
67
68 /**
69 * Constructor that initialises both fields, should be used only for
70 * initialiser-list initialisation in code that needs to compile as both C
71 * and C++.
72 */
77
78 /**
79 * Update this timeout if `time` ticks have elapsed. This function
80 * saturates the values on overflow.
81 */
82 inline void elapse(Ticks time)
83 {
84 if (__builtin_add_overflow(time, elapsed, &elapsed))
85 {
87 }
89 __builtin_sub_overflow(remaining, time, &remaining))
90 {
91 remaining = 0;
92 }
93 }
94
95 /**
96 * Helper indicating whether the owner of this timeout may block.
97 */
98 bool may_block()
99 {
100 return remaining > 0;
101 }
102#endif
104
105/**
106 * A union to hold timeouts that are passed down to end up in the scheduler.
107 * These are one of two things:
108 *
109 * - An absolute timeout, measured in cycles of some platform-specific timer
110 * (such as the `mtime` register) that increments at predictable rate.
111 * - A relative timeout counted in elapsed scheduler ticks.
112 *
113 * This is a tagged union, with the CHERI tag bit used to differentiate between
114 * the two cases.
115 */
116typedef union __if_c(__attribute__((transparent_union))) __TimeoutArgument
117{
118 /**
119 * An absolute timeout.
120 */
121 AbsoluteMonotonicTimeout absoluteTimeout;
122 /**
123 * A relative timeout.
124 */
125 Timeout *relativeTimeout;
126
127#ifdef __cplusplus
128 /// Permit implicit construction from absolute timeouts.
129 __TimeoutArgument(AbsoluteMonotonicTimeout absoluteTimeout)
130 : absoluteTimeout(absoluteTimeout)
131 {
132 }
133 /// Permit implicit construction from relative timeouts.
134 __TimeoutArgument(Timeout *relativeTimeout)
135 : relativeTimeout(relativeTimeout)
136 {
137 }
138
139 /**
140 * Returns true if this is a relative timeout, false otherwise.
141 */
142 bool is_relative()
143 {
144 return cheri_is_valid(relativeTimeout);
145 }
146
147 /**
148 * Returns true if this is a valid timeout, false otherwise.
149 */
150 bool is_valid();
151
152 /**
153 * Returns true if the timeout is in the future (and therefore may still
154 * block), false otherwise.
155 */
156 bool may_block();
157
158 /**
159 * If this is a relative timeout, elapse the specified number of ticks.
160 * Otherwise, do nothing.
161 */
162 void elapse(Ticks ticks);
163
164 /**
165 * Take any ticks that have been counted towards `source` and apply it to
166 * `destination`. If `destination` is not a tick-counting relative timeout,
167 * do nothing.
168 *
169 * This is intended for short-yielding operations, where a compartment
170 * wishes to yield for a small amount of time (typically one tick) and then
171 * retry some operation, rather than waiting for the whole time that the
172 * caller has allowed them to wait. The caller-provided timeout may be
173 * relative or absolute, but the timeout for the short sleep is local to the
174 * compartment doing the short sleep and so is always a `Timeout`.
175 */
176 void elapse_from(Timeout &source)
177 {
178 elapse(source.elapsed);
179 }
180#endif
181} TimeoutArgument;
182
183_Static_assert(sizeof(TimeoutArgument) == sizeof(void *),
184 "Timeout arguments should be passable in a single register");
185
186/**
187 * Check that the argument is a valid pointer to a `Timeout` structure. This
188 * must have read/write permissions, be unsealed, and must not be a heap
189 * address.
190 *
191 * This function is not recommended for new code. The more general
192 * `timeout_is_valid` call checks that this is a valid timeout argument.
193 */
194bool __cheri_libcall check_timeout_pointer(const struct Timeout *timeout);
195
196/**
197 * Helper to determine whether a timeout's `relativeTimeout` alternative is the
198 * active one.
199 */
200__if_c(static) inline bool timeout_is_relative(TimeoutArgument timeout)
201{
202 return cheri_is_valid(timeout.relativeTimeout);
203}
204
205/**
206 * Returns true if this is a valid `TimeoutArgument`: either an untagged value
207 * or a valid pointer to a `Timeout`.
208 */
209bool __cheriot_libcall timeout_is_valid(TimeoutArgument timeout);
210
211/**
212 * Returns true if the timeout is in the past (for relative timeouts, if there
213 * is no remaining time), false otherwise.
214 */
215bool __cheriot_libcall timeout_has_expired(TimeoutArgument timeout);
216
217/**
218 * Returns true if the timeout is in the future (and therefore may still
219 * block), false otherwise.
220 */
221__if_c(static) inline bool timeout_may_block(TimeoutArgument timeout)
222{
223 return !timeout_has_expired(timeout);
224}
225
226/**
227 * If this is a relative timeout, elapse the specified number of ticks.
228 * Otherwise, do nothing.
229 */
230void __cheriot_libcall timeout_elapse(TimeoutArgument timeout, Ticks ticks);
231
232/**
233 * Take any ticks that have been counted towards `source` and apply it to
234 * `destination`. If `destination` is not a tick-counting relative timeout, do
235 * nothing.
236 *
237 * This is intended for short-yielding operations, where a compartment wishes
238 * to yield for a small amount of time (typically one tick) and then retry some
239 * operation, rather than waiting for the whole time that the caller has
240 * allowed them to wait. The caller-provided timeout may be relative or
241 * absolute, but the timeout for the short sleep is local to the compartment
242 * doing the short sleep and so is always a `Timeout`.
243 */
244__if_c(static) inline void timeout_elapse_from(TimeoutArgument destination,
245 Timeout *source)
246{
247 timeout_elapse(destination, source->elapsed);
248}
249
250#ifdef __cplusplus
251inline bool TimeoutArgument::is_valid()
252{
253 return timeout_is_valid(*this);
254}
255inline bool TimeoutArgument::may_block()
256{
257 return timeout_may_block(*this);
258}
259inline void TimeoutArgument::elapse(Ticks ticks)
260{
261 return timeout_elapse(*this, ticks);
262}
263#endif
` defines the interface for the timer that is used to implement a monotonic clock.
Structure representing a timeout.
Definition timeout.h:47
bool may_block()
Helper indicating whether the owner of this timeout may block.
Definition timeout.h:98
Ticks elapsed
The time that has elapsed during blocking operations for this timeout structure.
Definition timeout.h:54
void elapse(Ticks time)
Update this timeout if time ticks have elapsed.
Definition timeout.h:82
Timeout(Ticks elapsed, Ticks remaining)
Constructor that initialises both fields, should be used only for initialiser-list initialisation in ...
Definition timeout.h:73
Ticks remaining
The remaining time.
Definition timeout.h:60
Timeout(Ticks time)
Constructor, initialises this structure to allow time ticks to elapse.
Definition timeout.h:66
bool __cheri_libcall check_timeout_pointer(const struct Timeout *timeout)
Check that the argument is a valid pointer to a Timeout structure.
uint32_t Ticks
Quantity used for measuring time for timeouts.
Definition timeout.h:19
uint64_t AbsoluteMonotonicTimeout
Type used for absolute timeouts.
Definition timeout.h:28
struct Timeout Timeout
Structure representing a timeout.
static const Ticks UnlimitedTimeout
Value indicating an unbounded timeout for use with Timeout.
Definition timeout.h:22
bool __cheriot_libcall timeout_has_expired(TimeoutArgument timeout)
Returns true if the timeout is in the past (for relative timeouts, if there is no remaining time),...
void __cheriot_libcall timeout_elapse(TimeoutArgument timeout, Ticks ticks)
If this is a relative timeout, elapse the specified number of ticks.
union __if_c() __TimeoutArgument
A union to hold timeouts that are passed down to end up in the scheduler.
Definition timeout.h:116
bool __cheriot_libcall timeout_is_valid(TimeoutArgument timeout)
Returns true if this is a valid TimeoutArgument: either an untagged value or a valid pointer to a Tim...
static const AbsoluteMonotonicTimeout TimeoutNoWait
Timeout value for not blocking.
Definition timeout.h:35
static const AbsoluteMonotonicTimeout TimeoutWaitForever
Timeout value for unlimited blocking.
Definition timeout.h:31