Coverage Report

Created: 2026-10-28 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl35/ssl/quic/quic_reactor.c
Line
Count
Source
1
/*
2
 * Copyright 2022-2026 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
#include "internal/quic_reactor.h"
10
#include "internal/common.h"
11
#include "internal/thread_arch.h"
12
#include <assert.h>
13
14
#if defined(OPENSSL_SYS_WINDOWS)
15
#include <winsock2.h>
16
#include <mstcpip.h>
17
#include <mswsock.h>
18
#endif
19
20
/*
21
 * Core I/O Reactor Framework
22
 * ==========================
23
 */
24
static void rtor_notify_other_threads(QUIC_REACTOR *rtor);
25
26
int ossl_quic_reactor_init(QUIC_REACTOR *rtor,
27
    void (*tick_cb)(QUIC_TICK_RESULT *res, void *arg,
28
        uint32_t flags),
29
    void *tick_cb_arg,
30
    CRYPTO_MUTEX *mutex,
31
    OSSL_TIME initial_tick_deadline,
32
    uint64_t flags)
33
43.2k
{
34
43.2k
    rtor->poll_r.type = BIO_POLL_DESCRIPTOR_TYPE_NONE;
35
43.2k
    rtor->poll_w.type = BIO_POLL_DESCRIPTOR_TYPE_NONE;
36
43.2k
    rtor->net_read_desired = 0;
37
43.2k
    rtor->net_write_desired = 0;
38
43.2k
    rtor->can_poll_r = 0;
39
43.2k
    rtor->can_poll_w = 0;
40
43.2k
    rtor->tick_deadline = initial_tick_deadline;
41
42
43.2k
    rtor->tick_cb = tick_cb;
43
43.2k
    rtor->tick_cb_arg = tick_cb_arg;
44
43.2k
    rtor->mutex = mutex;
45
46
43.2k
    rtor->cur_blocking_waiters = 0;
47
43.2k
    rtor->notify_generation = 0;
48
49
43.2k
    if ((flags & QUIC_REACTOR_FLAG_USE_NOTIFIER) != 0) {
50
0
        if (!ossl_rio_notifier_init(&rtor->notifier))
51
0
            return 0;
52
53
0
        if ((rtor->notifier_cv = ossl_crypto_condvar_new()) == NULL) {
54
0
            ossl_rio_notifier_cleanup(&rtor->notifier);
55
0
            return 0;
56
0
        }
57
58
0
        rtor->have_notifier = 1;
59
43.2k
    } else {
60
43.2k
        rtor->have_notifier = 0;
61
43.2k
    }
62
63
43.2k
    return 1;
64
43.2k
}
65
66
void ossl_quic_reactor_cleanup(QUIC_REACTOR *rtor)
67
43.2k
{
68
43.2k
    if (rtor == NULL)
69
0
        return;
70
71
43.2k
    if (rtor->have_notifier) {
72
0
        ossl_rio_notifier_cleanup(&rtor->notifier);
73
0
        rtor->have_notifier = 0;
74
75
0
        ossl_crypto_condvar_free(&rtor->notifier_cv);
76
0
    }
77
43.2k
}
78
79
#if defined(OPENSSL_SYS_WINDOWS)
80
81
/* Work around for MinGW builds. */
82
#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET)
83
#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15)
84
#endif
85
86
/*
87
 * On Windows recvfrom() may return WSAECONNRESET when destination port
88
 * used in preceding call to sendto() is no longer reachable. The reset
89
 * error received from UDP socket takes the whole port down. This behavior
90
 * must be suppressed for QUIC protocol so QUIC applications may rely on
91
 * QUIC protocol itself to detect network failures.
92
 */
93
static void rtor_configure_winsock(BIO_POLL_DESCRIPTOR *bpd)
94
{
95
    BOOL bNewBehavior = FALSE;
96
    DWORD dwBytesReturned = 0;
97
98
    if (bpd->type == BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) {
99
        WSAIoctl(bpd->value.fd, SIO_UDP_CONNRESET, &bNewBehavior,
100
            sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, NULL);
101
        WSAIoctl(bpd->value.fd, SIO_UDP_NETRESET, &bNewBehavior,
102
            sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, NULL);
103
    }
104
}
105
#endif
106
107
void ossl_quic_reactor_set_poll_r(QUIC_REACTOR *rtor, const BIO_POLL_DESCRIPTOR *r)
108
43.9M
{
109
43.9M
    if (r == NULL)
110
0
        rtor->poll_r.type = BIO_POLL_DESCRIPTOR_TYPE_NONE;
111
43.9M
    else
112
43.9M
        rtor->poll_r = *r;
113
114
#if defined(OPENSSL_SYS_WINDOWS)
115
    rtor_configure_winsock(&rtor->poll_r);
116
#endif
117
118
43.9M
    rtor->can_poll_r
119
43.9M
        = ossl_quic_reactor_can_support_poll_descriptor(rtor, &rtor->poll_r);
120
43.9M
}
121
122
void ossl_quic_reactor_set_poll_w(QUIC_REACTOR *rtor, const BIO_POLL_DESCRIPTOR *w)
123
43.9M
{
124
43.9M
    if (w == NULL)
125
0
        rtor->poll_w.type = BIO_POLL_DESCRIPTOR_TYPE_NONE;
126
43.9M
    else
127
43.9M
        rtor->poll_w = *w;
128
129
#if defined(OPENSSL_SYS_WINDOWS)
130
    rtor_configure_winsock(&rtor->poll_w);
131
#endif
132
133
43.9M
    rtor->can_poll_w
134
43.9M
        = ossl_quic_reactor_can_support_poll_descriptor(rtor, &rtor->poll_w);
135
43.9M
}
136
137
const BIO_POLL_DESCRIPTOR *ossl_quic_reactor_get_poll_r(const QUIC_REACTOR *rtor)
138
0
{
139
0
    return &rtor->poll_r;
140
0
}
141
142
const BIO_POLL_DESCRIPTOR *ossl_quic_reactor_get_poll_w(const QUIC_REACTOR *rtor)
143
0
{
144
0
    return &rtor->poll_w;
145
0
}
146
147
int ossl_quic_reactor_can_support_poll_descriptor(const QUIC_REACTOR *rtor,
148
    const BIO_POLL_DESCRIPTOR *d)
149
87.8M
{
150
87.8M
    return d->type == BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD;
151
87.8M
}
152
153
int ossl_quic_reactor_can_poll_r(const QUIC_REACTOR *rtor)
154
104M
{
155
104M
    return rtor->can_poll_r;
156
104M
}
157
158
int ossl_quic_reactor_can_poll_w(const QUIC_REACTOR *rtor)
159
99.7M
{
160
99.7M
    return rtor->can_poll_w;
161
99.7M
}
162
163
int ossl_quic_reactor_net_read_desired(QUIC_REACTOR *rtor)
164
0
{
165
0
    return rtor->net_read_desired;
166
0
}
167
168
int ossl_quic_reactor_net_write_desired(QUIC_REACTOR *rtor)
169
0
{
170
0
    return rtor->net_write_desired;
171
0
}
172
173
OSSL_TIME ossl_quic_reactor_get_tick_deadline(QUIC_REACTOR *rtor)
174
75.4M
{
175
75.4M
    return rtor->tick_deadline;
176
75.4M
}
177
178
int ossl_quic_reactor_tick(QUIC_REACTOR *rtor, uint32_t flags)
179
60.4M
{
180
60.4M
    QUIC_TICK_RESULT res = { 0 };
181
182
    /*
183
     * Note that the tick callback cannot fail; this is intentional. Arguably it
184
     * does not make that much sense for ticking to 'fail' (in the sense of an
185
     * explicit error indicated to the user) because ticking is by its nature
186
     * best effort. If something fatal happens with a connection we can report
187
     * it on the next actual application I/O call.
188
     */
189
60.4M
    rtor->tick_cb(&res, rtor->tick_cb_arg, flags);
190
191
60.4M
    rtor->net_read_desired = res.net_read_desired;
192
60.4M
    rtor->net_write_desired = res.net_write_desired;
193
60.4M
    rtor->tick_deadline = res.tick_deadline;
194
60.4M
    if (res.notify_other_threads)
195
2.48M
        rtor_notify_other_threads(rtor);
196
197
60.4M
    return 1;
198
60.4M
}
199
200
RIO_NOTIFIER *ossl_quic_reactor_get0_notifier(QUIC_REACTOR *rtor)
201
0
{
202
0
    return rtor->have_notifier ? &rtor->notifier : NULL;
203
0
}
204
205
/*
206
 * Blocking I/O Adaptation Layer
207
 * =============================
208
 */
209
210
/*
211
 * Utility which can be used to poll on up to two FDs. This is designed to
212
 * support use of split FDs (e.g. with SSL_set_rfd and SSL_set_wfd where
213
 * different FDs are used for read and write).
214
 *
215
 * Generally use of poll(2) is preferred where available. Windows, however,
216
 * hasn't traditionally offered poll(2), only select(2). WSAPoll() was
217
 * introduced in Vista but has seemingly been buggy until relatively recent
218
 * versions of Windows 10. Moreover we support XP so this is not a suitable
219
 * target anyway. However, the traditional issues with select(2) turn out not to
220
 * be an issue on Windows; whereas traditional *NIX select(2) uses a bitmap of
221
 * FDs (and thus is limited in the magnitude of the FDs expressible), Windows
222
 * select(2) is very different. In Windows, socket handles are not allocated
223
 * contiguously from zero and thus this bitmap approach was infeasible. Thus in
224
 * adapting the Berkeley sockets API to Windows a different approach was taken
225
 * whereby the fd_set contains a fixed length array of socket handles and an
226
 * integer indicating how many entries are valid; thus Windows select()
227
 * ironically is actually much more like *NIX poll(2) than *NIX select(2). In
228
 * any case, this means that the relevant limit for Windows select() is the
229
 * number of FDs being polled, not the magnitude of those FDs. Since we only
230
 * poll for two FDs here, this limit does not concern us.
231
 *
232
 * Usage: rfd and wfd may be the same or different. Either or both may also be
233
 * -1. If rfd_want_read is 1, rfd is polled for readability, and if
234
 * wfd_want_write is 1, wfd is polled for writability. Note that since any
235
 * passed FD is always polled for error conditions, setting rfd_want_read=0 and
236
 * wfd_want_write=0 is not the same as passing -1 for both FDs.
237
 *
238
 * deadline is a timestamp to return at. If it is ossl_time_infinite(), the call
239
 * never times out.
240
 *
241
 * Returns 0 on error and 1 on success. Timeout expiry is considered a success
242
 * condition. We don't elaborate our return values here because the way we are
243
 * actually using this doesn't currently care.
244
 *
245
 * If mutex is non-NULL, it is assumed to be held for write and is unlocked for
246
 * the duration of the call.
247
 *
248
 * Precondition:   mutex is NULL or is held for write (unchecked)
249
 * Postcondition:  mutex is NULL or is held for write (unless
250
 *                   CRYPTO_THREAD_write_lock fails)
251
 */
252
static int poll_two_fds(int rfd, int rfd_want_read,
253
    int wfd, int wfd_want_write,
254
    int notify_rfd,
255
    OSSL_TIME deadline,
256
    CRYPTO_MUTEX *mutex)
257
0
{
258
#if defined(OPENSSL_SYS_WINDOWS) || !defined(POLLIN)
259
    fd_set rfd_set, wfd_set, efd_set;
260
    OSSL_TIME now, timeout;
261
    struct timeval tv, *ptv;
262
    int maxfd, pres;
263
264
#ifndef OPENSSL_SYS_WINDOWS
265
    /*
266
     * On Windows there is no relevant limit to the magnitude of a fd value (see
267
     * above). On *NIX the fd_set uses a bitmap and we must check the limit.
268
     */
269
    if (rfd >= FD_SETSIZE || wfd >= FD_SETSIZE)
270
        return 0;
271
#endif
272
273
    FD_ZERO(&rfd_set);
274
    FD_ZERO(&wfd_set);
275
    FD_ZERO(&efd_set);
276
277
    if (rfd != INVALID_SOCKET && rfd_want_read)
278
        openssl_fdset(rfd, &rfd_set);
279
    if (wfd != INVALID_SOCKET && wfd_want_write)
280
        openssl_fdset(wfd, &wfd_set);
281
282
    /* Always check for error conditions. */
283
    if (rfd != INVALID_SOCKET)
284
        openssl_fdset(rfd, &efd_set);
285
    if (wfd != INVALID_SOCKET)
286
        openssl_fdset(wfd, &efd_set);
287
288
    /* Check for notifier FD readability. */
289
    if (notify_rfd != INVALID_SOCKET) {
290
        openssl_fdset(notify_rfd, &rfd_set);
291
        openssl_fdset(notify_rfd, &efd_set);
292
    }
293
294
    maxfd = rfd;
295
    if (wfd > maxfd)
296
        maxfd = wfd;
297
    if (notify_rfd > maxfd)
298
        maxfd = notify_rfd;
299
300
    if (!ossl_assert(rfd != INVALID_SOCKET || wfd != INVALID_SOCKET
301
            || !ossl_time_is_infinite(deadline)))
302
        /* Do not block forever; should not happen. */
303
        return 0;
304
305
    /*
306
     * The mutex dance (unlock/re-locak after poll/seclect) is
307
     * potentially problematic. This may create a situation when
308
     * two threads arrive to select/poll with the same file
309
     * descriptors. We just need to be aware of this.
310
     */
311
#if defined(OPENSSL_THREADS)
312
    if (mutex != NULL)
313
        ossl_crypto_mutex_unlock(mutex);
314
#endif
315
316
    do {
317
        /*
318
         * select expects a timeout, not a deadline, so do the conversion.
319
         * Update for each call to ensure the correct value is used if we repeat
320
         * due to EINTR.
321
         */
322
        if (ossl_time_is_infinite(deadline)) {
323
            ptv = NULL;
324
        } else {
325
            now = ossl_time_now();
326
            /*
327
             * ossl_time_subtract saturates to zero so we don't need to check if
328
             * now > deadline.
329
             */
330
            timeout = ossl_time_subtract(deadline, now);
331
            tv = ossl_time_to_timeval(timeout);
332
            ptv = &tv;
333
        }
334
335
        pres = select(maxfd + 1, &rfd_set, &wfd_set, &efd_set, ptv);
336
    } while (pres == -1 && get_last_socket_error_is_eintr());
337
338
#if defined(OPENSSL_THREADS)
339
    if (mutex != NULL)
340
        ossl_crypto_mutex_lock(mutex);
341
#endif
342
343
    return pres < 0 ? 0 : 1;
344
#else
345
0
    int pres, timeout_ms;
346
0
    OSSL_TIME now, timeout;
347
0
    struct pollfd pfds[3] = { 0 };
348
0
    size_t npfd = 0;
349
350
0
    if (rfd == wfd) {
351
0
        pfds[npfd].fd = rfd;
352
0
        pfds[npfd].events = (rfd_want_read ? POLLIN : 0)
353
0
            | (wfd_want_write ? POLLOUT : 0);
354
0
        if (rfd >= 0 && pfds[npfd].events != 0)
355
0
            ++npfd;
356
0
    } else {
357
0
        pfds[npfd].fd = rfd;
358
0
        pfds[npfd].events = (rfd_want_read ? POLLIN : 0);
359
0
        if (rfd >= 0 && pfds[npfd].events != 0)
360
0
            ++npfd;
361
362
0
        pfds[npfd].fd = wfd;
363
0
        pfds[npfd].events = (wfd_want_write ? POLLOUT : 0);
364
0
        if (wfd >= 0 && pfds[npfd].events != 0)
365
0
            ++npfd;
366
0
    }
367
368
0
    if (notify_rfd >= 0) {
369
0
        pfds[npfd].fd = notify_rfd;
370
0
        pfds[npfd].events = POLLIN;
371
0
        ++npfd;
372
0
    }
373
374
0
    if (!ossl_assert(npfd != 0 || !ossl_time_is_infinite(deadline)))
375
        /* Do not block forever; should not happen. */
376
0
        return 0;
377
378
0
#if defined(OPENSSL_THREADS)
379
0
    if (mutex != NULL)
380
0
        ossl_crypto_mutex_unlock(mutex);
381
0
#endif
382
383
0
    do {
384
0
        if (ossl_time_is_infinite(deadline)) {
385
0
            timeout_ms = -1;
386
0
        } else {
387
0
            now = ossl_time_now();
388
0
            timeout = ossl_time_subtract(deadline, now);
389
0
            timeout_ms = ossl_time2ms(timeout);
390
0
        }
391
392
0
        pres = poll(pfds, npfd, timeout_ms);
393
0
    } while (pres == -1 && get_last_socket_error_is_eintr());
394
395
0
#if defined(OPENSSL_THREADS)
396
0
    if (mutex != NULL)
397
0
        ossl_crypto_mutex_lock(mutex);
398
0
#endif
399
400
0
    return pres < 0 ? 0 : 1;
401
0
#endif
402
0
}
403
404
static int poll_descriptor_to_fd(const BIO_POLL_DESCRIPTOR *d, int *fd)
405
0
{
406
0
    if (d == NULL || d->type == BIO_POLL_DESCRIPTOR_TYPE_NONE) {
407
0
        *fd = INVALID_SOCKET;
408
0
        return 1;
409
0
    }
410
411
0
    if (d->type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD
412
0
        || d->value.fd == INVALID_SOCKET)
413
0
        return 0;
414
415
0
    *fd = d->value.fd;
416
0
    return 1;
417
0
}
418
419
/*
420
 * Poll up to two abstract poll descriptors, as well as an optional notify FD.
421
 * Currently we only support poll descriptors which represent FDs.
422
 *
423
 * If mutex is non-NULL, it is assumed be a lock currently held for write and is
424
 * unlocked for the duration of any wait.
425
 *
426
 * Precondition:   mutex is NULL or is held for write (unchecked)
427
 * Postcondition:  mutex is NULL or is held for write (unless
428
 *                   CRYPTO_THREAD_write_lock fails)
429
 */
430
static int poll_two_descriptors(const BIO_POLL_DESCRIPTOR *r, int r_want_read,
431
    const BIO_POLL_DESCRIPTOR *w, int w_want_write,
432
    int notify_rfd,
433
    OSSL_TIME deadline,
434
    CRYPTO_MUTEX *mutex)
435
0
{
436
0
    int rfd, wfd;
437
438
0
    if (!poll_descriptor_to_fd(r, &rfd)
439
0
        || !poll_descriptor_to_fd(w, &wfd))
440
0
        return 0;
441
442
0
    return poll_two_fds(rfd, r_want_read, wfd, w_want_write,
443
0
        notify_rfd, deadline, mutex);
444
0
}
445
446
/*
447
 * Notify other threads currently blocking in
448
 * ossl_quic_reactor_block_until_pred() calls that a predicate they are using
449
 * might now be met due to state changes.
450
 *
451
 * This function must be called after state changes which might cause a
452
 * predicate in another thread to now be met (i.e., ticking). It is a no-op if
453
 * inter-thread notification is not being used.
454
 *
455
 * The reactor mutex must be held while calling this function.
456
 */
457
static void rtor_notify_other_threads(QUIC_REACTOR *rtor)
458
2.48M
{
459
2.48M
    if (!rtor->have_notifier)
460
2.48M
        return;
461
462
    /*
463
     * This function is called when we have done anything on this thread which
464
     * might allow a predicate for a block_until_pred call on another thread to
465
     * now be met.
466
     *
467
     * When this happens, we need to wake those threads using the notifier.
468
     * However, we do not want to wake *this* thread (if/when it subsequently
469
     * enters block_until_pred) due to the notifier FD becoming readable.
470
     * Therefore, signal the notifier, and use a CV to detect when all other
471
     * threads have woken.
472
     */
473
474
0
    if (rtor->cur_blocking_waiters == 0)
475
        /* Nothing to do in this case. */
476
0
        return;
477
478
    /* Signal the notifier to wake up all threads. */
479
0
    if (!rtor->signalled_notifier) {
480
0
        ossl_rio_notifier_signal(&rtor->notifier);
481
0
        rtor->signalled_notifier = 1;
482
0
    }
483
484
    /*
485
     * Wait on the CV until all threads have finished the first phase of the
486
     * wakeup process and the last thread out has taken responsibility for
487
     * unsignalling the notifier.
488
     */
489
0
    while (rtor->signalled_notifier)
490
0
        ossl_crypto_condvar_wait(rtor->notifier_cv, rtor->mutex);
491
0
}
492
493
/*
494
 * Block until a predicate function evaluates to true.
495
 *
496
 * If mutex is non-NULL, it is assumed be a lock currently held for write and is
497
 * unlocked for the duration of any wait.
498
 *
499
 * Precondition:   Must hold channel write lock (unchecked)
500
 * Precondition:   mutex is NULL or is held for write (unchecked)
501
 * Postcondition:  mutex is NULL or is held for write (unless
502
 *                   CRYPTO_THREAD_write_lock fails)
503
 */
504
int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor,
505
    int (*pred)(void *arg), void *pred_arg,
506
    uint32_t flags)
507
0
{
508
0
    int res, net_read_desired, net_write_desired, notifier_fd;
509
0
    OSSL_TIME tick_deadline;
510
511
0
    notifier_fd
512
0
        = (rtor->have_notifier ? ossl_rio_notifier_as_fd(&rtor->notifier)
513
0
                               : INVALID_SOCKET);
514
515
0
    for (;;) {
516
0
        if ((flags & SKIP_FIRST_TICK) != 0)
517
0
            flags &= ~SKIP_FIRST_TICK;
518
0
        else
519
            /* best effort */
520
0
            ossl_quic_reactor_tick(rtor, 0);
521
522
0
        if ((res = pred(pred_arg)) != 0)
523
0
            return res;
524
525
0
        net_read_desired = ossl_quic_reactor_net_read_desired(rtor);
526
0
        net_write_desired = ossl_quic_reactor_net_write_desired(rtor);
527
0
        tick_deadline = ossl_quic_reactor_get_tick_deadline(rtor);
528
0
        if (!net_read_desired && !net_write_desired
529
0
            && ossl_time_is_infinite(tick_deadline))
530
            /* Can't wait if there is nothing to wait for. */
531
0
            return 0;
532
533
0
        ossl_quic_reactor_enter_blocking_section(rtor);
534
535
0
        res = poll_two_descriptors(ossl_quic_reactor_get_poll_r(rtor),
536
0
            net_read_desired,
537
0
            ossl_quic_reactor_get_poll_w(rtor),
538
0
            net_write_desired,
539
0
            notifier_fd,
540
0
            tick_deadline,
541
0
            rtor->mutex);
542
543
        /*
544
         * We have now exited the OS poller call. We may have
545
         * (rtor->signalled_notifier), and other threads may still be blocking.
546
         * This means that cur_blocking_waiters may still be non-zero. As such,
547
         * we cannot unsignal the notifier until all threads have had an
548
         * opportunity to wake up.
549
         *
550
         * At the same time, we cannot unsignal in the case where
551
         * cur_blocking_waiters is now zero because this condition may not occur
552
         * reliably. Consider the following scenario:
553
         *
554
         *   T1 enters block_until_pred, cur_blocking_waiters -> 1
555
         *   T2 enters block_until_pred, cur_blocking_waiters -> 2
556
         *   T3 enters block_until_pred, cur_blocking_waiters -> 3
557
         *
558
         *   T4 enters block_until_pred, does not block, ticks,
559
         *     sees that cur_blocking_waiters > 0 and signals the notifier
560
         *
561
         *   T3 wakes, cur_blocking_waiters -> 2
562
         *   T3 predicate is not satisfied, cur_blocking_waiters -> 3, block again
563
         *
564
         *   Notifier is still signalled, so T3 immediately wakes again
565
         *   and is stuck repeating the above steps.
566
         *
567
         *   T1, T2 are also woken by the notifier but never see
568
         *   cur_blocking_waiters drop to 0, so never unsignal the notifier.
569
         *
570
         * As such, a two phase approach is chosen when designalling the
571
         * notifier:
572
         *
573
         *   First, all of the poll_two_descriptor calls on all threads are
574
         *   allowed to exit due to the notifier being signalled.
575
         *
576
         *   Second, the thread which happened to be the one which decremented
577
         *   cur_blocking_waiters to 0 unsignals the notifier and is then
578
         *   responsible for broadcasting to a CV to indicate to the other
579
         *   threads that the synchronised wakeup has been completed. Other
580
         *   threads wait for this CV to be signalled.
581
         *
582
         */
583
0
        ossl_quic_reactor_leave_blocking_section(rtor);
584
585
0
        if (!res)
586
            /*
587
             * We don't actually care why the call succeeded (timeout, FD
588
             * readiness), we just call reactor_tick and start trying to do I/O
589
             * things again. If poll_two_fds returns 0, this is some other
590
             * non-timeout failure and we should stop here.
591
             *
592
             * TODO(QUIC FUTURE): In the future we could avoid unnecessary
593
             * syscalls by not retrying network I/O that isn't ready based
594
             * on the result of the poll call. However this might be difficult
595
             * because it requires we do the call to poll(2) or equivalent
596
             * syscall ourselves, whereas in the general case the application
597
             * does the polling and just calls SSL_handle_events().
598
             * Implementing this optimisation in the future will probably
599
             * therefore require API changes.
600
             */
601
0
            return 0;
602
0
    }
603
604
0
    return res;
605
0
}
606
607
void ossl_quic_reactor_enter_blocking_section(QUIC_REACTOR *rtor)
608
0
{
609
0
    ++rtor->cur_blocking_waiters;
610
0
}
611
612
int ossl_quic_reactor_deregister_blocking_section(QUIC_REACTOR *rtor,
613
    uint64_t *gen)
614
0
{
615
0
    assert(rtor->cur_blocking_waiters > 0);
616
0
    --rtor->cur_blocking_waiters;
617
618
    /* The last waiter drains the notifier and advances its signal generation. */
619
0
    if (rtor->have_notifier && rtor->signalled_notifier
620
0
        && rtor->cur_blocking_waiters == 0) {
621
0
        ossl_rio_notifier_unsignal(&rtor->notifier);
622
0
        rtor->signalled_notifier = 0;
623
0
        ++rtor->notify_generation;
624
0
        ossl_crypto_condvar_broadcast(rtor->notifier_cv);
625
0
    }
626
627
    /* Remaining waiters require phase 2 for the current signal generation. */
628
0
    if (rtor->have_notifier && rtor->signalled_notifier) {
629
0
        *gen = rtor->notify_generation;
630
0
        return 1;
631
0
    }
632
633
0
    return 0;
634
0
}
635
636
/*
637
 * Wait only for the signal cycle observed during deregistration.
638
 * Precondition: reactor mutex held.
639
 */
640
static void rtor_wait_for_notifier_clear_locked(QUIC_REACTOR *rtor,
641
    uint64_t gen)
642
0
{
643
0
    if (rtor->have_notifier)
644
0
        while (rtor->signalled_notifier
645
0
            && rtor->notify_generation == gen)
646
0
            ossl_crypto_condvar_wait(rtor->notifier_cv, rtor->mutex);
647
0
}
648
649
void ossl_quic_reactor_wait_for_notifier_clear(QUIC_REACTOR *rtor,
650
    uint64_t gen)
651
0
{
652
0
    ossl_crypto_mutex_lock(rtor->mutex);
653
0
    rtor_wait_for_notifier_clear_locked(rtor, gen);
654
0
    ossl_crypto_mutex_unlock(rtor->mutex);
655
0
}
656
657
void ossl_quic_reactor_leave_blocking_section(QUIC_REACTOR *rtor)
658
0
{
659
0
    uint64_t gen;
660
661
    /* Caller holds the reactor mutex across both phases. */
662
0
    if (ossl_quic_reactor_deregister_blocking_section(rtor, &gen))
663
0
        rtor_wait_for_notifier_clear_locked(rtor, gen);
664
0
}