Coverage Report

Created: 2026-02-14 07:20

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