/src/openssl/include/internal/quic_reactor.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | | * this file except in compliance with the License. You can obtain a copy |
6 | | * in the file LICENSE in the source distribution or at |
7 | | * https://www.openssl.org/source/license.html |
8 | | */ |
9 | | #ifndef OSSL_QUIC_REACTOR_H |
10 | | # define OSSL_QUIC_REACTOR_H |
11 | | |
12 | | # include "internal/time.h" |
13 | | # include "internal/sockets.h" |
14 | | # include "internal/quic_predef.h" |
15 | | # include "internal/thread_arch.h" |
16 | | # include "internal/rio_notifier.h" |
17 | | # include <openssl/bio.h> |
18 | | |
19 | | # ifndef OPENSSL_NO_QUIC |
20 | | |
21 | | /* |
22 | | * Core I/O Reactor Framework |
23 | | * ========================== |
24 | | * |
25 | | * Manages use of async network I/O which the QUIC stack is built on. The core |
26 | | * mechanic looks like this: |
27 | | * |
28 | | * - There is a pollable FD for both the read and write side respectively. |
29 | | * Readability and writeability of these FDs respectively determines when |
30 | | * network I/O is available. |
31 | | * |
32 | | * - The reactor can export these FDs to the user, as well as flags indicating |
33 | | * whether the user should listen for readability, writeability, or neither. |
34 | | * |
35 | | * - The reactor can export a timeout indication to the user, indicating when |
36 | | * the reactor should be called (via libssl APIs) regardless of whether |
37 | | * the network socket has become ready. |
38 | | * |
39 | | * The reactor is based around a tick callback which is essentially the mutator |
40 | | * function. The mutator attempts to do whatever it can, attempting to perform |
41 | | * network I/O to the extent currently feasible. When done, the mutator returns |
42 | | * information to the reactor indicating when it should be woken up again: |
43 | | * |
44 | | * - Should it be woken up when network RX is possible? |
45 | | * - Should it be woken up when network TX is possible? |
46 | | * - Should it be woken up no later than some deadline X? |
47 | | * |
48 | | * The intention is that ALL I/O-related SSL_* functions with side effects (e.g. |
49 | | * SSL_read/SSL_write) consist of three phases: |
50 | | * |
51 | | * - Optionally mutate the QUIC machine's state. |
52 | | * - Optionally tick the QUIC reactor. |
53 | | * - Optionally mutate the QUIC machine's state. |
54 | | * |
55 | | * For example, SSL_write is a mutation (appending to a stream buffer) followed |
56 | | * by an optional tick (generally expected as we may want to send the data |
57 | | * immediately, though not strictly needed if transmission is being deferred due |
58 | | * to Nagle's algorithm, etc.). |
59 | | * |
60 | | * SSL_read is also a mutation and in principle does not need to tick the |
61 | | * reactor, but it generally will anyway to ensure that the reactor is regularly |
62 | | * ticked by an application which is only reading and not writing. |
63 | | * |
64 | | * If the SSL object is being used in blocking mode, SSL_read may need to block |
65 | | * if no data is available yet, and SSL_write may need to block if buffers |
66 | | * are full. |
67 | | * |
68 | | * The internals of the QUIC I/O engine always use asynchronous I/O. If the |
69 | | * application desires blocking semantics, we handle this by adding a blocking |
70 | | * adaptation layer on top of our internal asynchronous I/O API as exposed by |
71 | | * the reactor interface. |
72 | | */ |
73 | | struct quic_tick_result_st { |
74 | | OSSL_TIME tick_deadline; |
75 | | char net_read_desired; |
76 | | char net_write_desired; |
77 | | char notify_other_threads; |
78 | | }; |
79 | | |
80 | | static ossl_inline ossl_unused void |
81 | | ossl_quic_tick_result_merge_into(QUIC_TICK_RESULT *r, |
82 | | const QUIC_TICK_RESULT *src) |
83 | 158 | { |
84 | 158 | r->net_read_desired = r->net_read_desired || src->net_read_desired; |
85 | 158 | r->net_write_desired = r->net_write_desired || src->net_write_desired; |
86 | 158 | r->notify_other_threads = r->notify_other_threads || src->notify_other_threads; |
87 | 158 | r->tick_deadline = ossl_time_min(r->tick_deadline, src->tick_deadline); |
88 | 158 | } Unexecuted instantiation: ssl_lib.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: t1_lib.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_impl.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_method.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_obj.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_port.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_reactor.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_reactor_wait_ctx.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_thread_assist.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: rec_layer_s3.c:ossl_quic_tick_result_merge_into Unexecuted instantiation: quic_channel.c:ossl_quic_tick_result_merge_into quic_engine.c:ossl_quic_tick_result_merge_into Line | Count | Source | 83 | 158 | { | 84 | 158 | r->net_read_desired = r->net_read_desired || src->net_read_desired; | 85 | 158 | r->net_write_desired = r->net_write_desired || src->net_write_desired; | 86 | 158 | r->notify_other_threads = r->notify_other_threads || src->notify_other_threads; | 87 | 158 | r->tick_deadline = ossl_time_min(r->tick_deadline, src->tick_deadline); | 88 | 158 | } |
Unexecuted instantiation: quic_rx_depack.c:ossl_quic_tick_result_merge_into |
89 | | |
90 | | struct quic_reactor_st { |
91 | | /* |
92 | | * BIO poll descriptors which can be polled. poll_r is a poll descriptor |
93 | | * which becomes readable when the QUIC state machine can potentially do |
94 | | * work, and poll_w is a poll descriptor which becomes writable when the |
95 | | * QUIC state machine can potentially do work. Generally, either of these |
96 | | * conditions means that SSL_tick() should be called, or another SSL |
97 | | * function which implicitly calls SSL_tick() (e.g. SSL_read/SSL_write()). |
98 | | */ |
99 | | BIO_POLL_DESCRIPTOR poll_r, poll_w; |
100 | | OSSL_TIME tick_deadline; /* ossl_time_infinite() if none currently applicable */ |
101 | | |
102 | | void (*tick_cb)(QUIC_TICK_RESULT *res, void *arg, uint32_t flags); |
103 | | void *tick_cb_arg; |
104 | | |
105 | | /* The mutex used for ticking. Not owned by the reactor. */ |
106 | | CRYPTO_MUTEX *mutex; |
107 | | |
108 | | /* Used to notify other threads. Valid only if have_notifier is set. */ |
109 | | RIO_NOTIFIER notifier; |
110 | | |
111 | | /* |
112 | | * Condvar to assist synchronising use of the notifier. Valid only if |
113 | | * have_notifier is set. |
114 | | */ |
115 | | CRYPTO_CONDVAR *notifier_cv; |
116 | | |
117 | | /* |
118 | | * Count of the current number of blocking waiters. Like everything else, |
119 | | * this is protected by the caller's mutex (i.e., the engine mutex). |
120 | | */ |
121 | | size_t cur_blocking_waiters; |
122 | | |
123 | | /* |
124 | | * These are true if we would like to know when we can read or write from |
125 | | * the network respectively. |
126 | | */ |
127 | | unsigned int net_read_desired : 1; |
128 | | unsigned int net_write_desired : 1; |
129 | | |
130 | | /* |
131 | | * Are the read and write poll descriptors we are currently configured with |
132 | | * things we can actually poll? |
133 | | */ |
134 | | unsigned int can_poll_r : 1; |
135 | | unsigned int can_poll_w : 1; |
136 | | |
137 | | /* 1 if notifier is present and initialised. */ |
138 | | unsigned int have_notifier : 1; |
139 | | |
140 | | /* 1 if a block_until_pred call has put the notifier in the signalled state. */ |
141 | | unsigned int signalled_notifier : 1; |
142 | | }; |
143 | | |
144 | | /* Create an OS notifier? */ |
145 | 79 | #define QUIC_REACTOR_FLAG_USE_NOTIFIER (1U << 0) |
146 | | |
147 | | int ossl_quic_reactor_init(QUIC_REACTOR *rtor, |
148 | | void (*tick_cb)(QUIC_TICK_RESULT *res, void *arg, |
149 | | uint32_t flags), |
150 | | void *tick_cb_arg, |
151 | | CRYPTO_MUTEX *mutex, |
152 | | OSSL_TIME initial_tick_deadline, |
153 | | uint64_t flags); |
154 | | |
155 | | void ossl_quic_reactor_cleanup(QUIC_REACTOR *rtor); |
156 | | |
157 | | void ossl_quic_reactor_set_poll_r(QUIC_REACTOR *rtor, |
158 | | const BIO_POLL_DESCRIPTOR *r); |
159 | | |
160 | | void ossl_quic_reactor_set_poll_w(QUIC_REACTOR *rtor, |
161 | | const BIO_POLL_DESCRIPTOR *w); |
162 | | |
163 | | const BIO_POLL_DESCRIPTOR *ossl_quic_reactor_get_poll_r(const QUIC_REACTOR *rtor); |
164 | | const BIO_POLL_DESCRIPTOR *ossl_quic_reactor_get_poll_w(const QUIC_REACTOR *rtor); |
165 | | |
166 | | int ossl_quic_reactor_can_poll_r(const QUIC_REACTOR *rtor); |
167 | | int ossl_quic_reactor_can_poll_w(const QUIC_REACTOR *rtor); |
168 | | |
169 | | int ossl_quic_reactor_can_support_poll_descriptor(const QUIC_REACTOR *rtor, |
170 | | const BIO_POLL_DESCRIPTOR *d); |
171 | | |
172 | | int ossl_quic_reactor_net_read_desired(QUIC_REACTOR *rtor); |
173 | | int ossl_quic_reactor_net_write_desired(QUIC_REACTOR *rtor); |
174 | | |
175 | | OSSL_TIME ossl_quic_reactor_get_tick_deadline(QUIC_REACTOR *rtor); |
176 | | |
177 | | /* |
178 | | * Do whatever work can be done, and as much work as can be done. This involves |
179 | | * e.g. seeing if we can read anything from the network (if we want to), seeing |
180 | | * if we can write anything to the network (if we want to), etc. |
181 | | * |
182 | | * If the CHANNEL_ONLY flag is set, this indicates that we should only |
183 | | * touch state which is synchronised by the channel mutex. |
184 | | */ |
185 | 0 | #define QUIC_REACTOR_TICK_FLAG_CHANNEL_ONLY (1U << 0) |
186 | | |
187 | | int ossl_quic_reactor_tick(QUIC_REACTOR *rtor, uint32_t flags); |
188 | | |
189 | | RIO_NOTIFIER *ossl_quic_reactor_get0_notifier(QUIC_REACTOR *rtor); |
190 | | |
191 | | /* |
192 | | * Blocking I/O Adaptation Layer |
193 | | * ============================= |
194 | | * |
195 | | * The blocking I/O adaptation layer implements blocking I/O on top of our |
196 | | * asynchronous core. |
197 | | */ |
198 | | |
199 | | /* |
200 | | * ossl_quic_reactor_block_until_pred |
201 | | * ---------------------------------- |
202 | | * |
203 | | * The core mechanism of the Blocking I/O Adaption Layer is block_until_pred(), |
204 | | * which does not return until pred() returns a value other than 0. The blocker |
205 | | * uses OS I/O synchronisation primitives (e.g. poll(2)) and ticks the reactor |
206 | | * until the predicate is satisfied. The blocker is not required to call pred() |
207 | | * more than once between tick calls. |
208 | | * |
209 | | * When pred returns a non-zero value, that value is returned by this function. |
210 | | * This can be used to allow pred() to indicate error conditions and short |
211 | | * circuit the blocking process. |
212 | | * |
213 | | * A return value of -1 is reserved for network polling errors. Therefore this |
214 | | * return value should not be used by pred() if ambiguity is not desired. Note |
215 | | * that the predicate function can always arrange its own output mechanism, for |
216 | | * example by passing a structure of its own as the argument. |
217 | | * |
218 | | * If the SKIP_FIRST_TICK flag is set, the first call to reactor_tick() before |
219 | | * the first call to pred() is skipped. This is useful if it is known that |
220 | | * ticking the reactor again will not be useful (e.g. because it has already |
221 | | * been done). |
222 | | * |
223 | | * This function assumes a write lock is held for the entire QUIC_CHANNEL. If |
224 | | * mutex is non-NULL, it must be a lock currently held for write; it will be |
225 | | * unlocked during any sleep, and then relocked for write afterwards. |
226 | | * |
227 | | * This function must not be called by a thread currently using |
228 | | * ossl_quic_reactor_(enter/leave)_blocking_section() as this function also uses |
229 | | * those functions (see below); it is assumed if a caller is using those |
230 | | * functions it is implementing blocking semantics itself. There is no need to |
231 | | * use those functions if using this function. |
232 | | * |
233 | | * Precondition: If a reactor mutex is being used, it must be held (unchecked) |
234 | | * Postcondition: If a reactor mutex is being used, it is held |
235 | | * Invariant: The current thread does not have an outstanding |
236 | | * ossl_quic_reactor_enter_blocking_section() call (unchecked) |
237 | | */ |
238 | 0 | #define SKIP_FIRST_TICK (1U << 0) |
239 | | |
240 | | int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor, |
241 | | int (*pred)(void *arg), void *pred_arg, |
242 | | uint32_t flags); |
243 | | |
244 | | /* |
245 | | * ossl_quic_reactor_(enter/leave)_blocking_section |
246 | | * ------------------------------------------------ |
247 | | * |
248 | | * This is used by blocking code outside of the reactor itself to inform the |
249 | | * reactor of when a thread begins or ends a blocking call. This is used by the |
250 | | * reactor so it knows if a tick means other threads might need to be woken up |
251 | | * via the notifier. The reactor mutex must be held while calling these |
252 | | * functions. |
253 | | * |
254 | | * The number of 'active' calls to these functions (i.e., the number of enter |
255 | | * calls which have yet to be matched with a subsequent leave call) must *at all |
256 | | * times* equal the number of threads blocking on the reactor. In other words, a |
257 | | * single thread is not permitted to use these functions "recursively". Failing |
258 | | * to adhere to this rule will result in deadlock. |
259 | | * |
260 | | * This means that if a caller has the concept of multiple concurrent blocking |
261 | | * calls on the same thread on the same reactor (which may occur in some |
262 | | * SSL_poll-related circumstances) it must do its own housekeeping to ensure it |
263 | | * only calls enter() once. See quic_reactor_wait_ctx.h for a utility which can |
264 | | * be used to accomplish this. |
265 | | * |
266 | | * ossl_quic_reactor_enter_blocking_section: |
267 | | * Precondition: The current thread does not have an outstanding |
268 | | * ossl_quic_reactor_enter_blocking_section() call (unchecked) |
269 | | * Postcondition: The current thread has an outstanding |
270 | | * ossl_quic_reactor_enter_blocking_section() call |
271 | | * |
272 | | * ossl_quic_reactor_leave_blocking_section: |
273 | | * Precondition: The current thread has an outstanding |
274 | | * ossl_quic_reactor_enter_blocking_section() call (unchecked) |
275 | | * Postcondition: The current thread does not have an outstanding |
276 | | * ossl_quic_reactor_enter_blocking_section() call |
277 | | * |
278 | | */ |
279 | | void ossl_quic_reactor_enter_blocking_section(QUIC_REACTOR *rtor); |
280 | | void ossl_quic_reactor_leave_blocking_section(QUIC_REACTOR *rtor); |
281 | | |
282 | | # endif |
283 | | |
284 | | #endif |