From: Hugo Landau Date: Mon, 13 May 2024 19:20:23 +0000 (+0100) Subject: QUIC REACTOR: Add support for external registration of blocking operations X-Git-Tag: openssl-3.5.0-alpha1~351 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5d3720dcb6d281238e7376a92ee4d6cac44d31f9;p=thirdparty%2Fopenssl.git QUIC REACTOR: Add support for external registration of blocking operations Reviewed-by: Matt Caswell Reviewed-by: Saša Nedvědický Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/25416) --- diff --git a/include/internal/quic_reactor.h b/include/internal/quic_reactor.h index bc5e5bd5f64..7dadb8b83de 100644 --- a/include/internal/quic_reactor.h +++ b/include/internal/quic_reactor.h @@ -224,14 +224,61 @@ RIO_NOTIFIER *ossl_quic_reactor_get0_notifier(QUIC_REACTOR *rtor); * mutex is non-NULL, it must be a lock currently held for write; it will be * unlocked during any sleep, and then relocked for write afterwards. * + * This function must not be called by a thread currently using + * ossl_quic_reactor_(enter/leave)_blocking_section() as this function also uses + * those functions (see below); it is assumed if a caller is using those + * functions it is implementing blocking semantics itself. There is no need to + * use those functions if using this function. + * * Precondition: If a reactor mutex is being used, it must be held (unchecked) * Postcondition: If a reactor mutex is being used, it is held + * Invariant: The current thread does not have an outstanding + * ossl_quic_reactor_enter_blocking_section() call (unchecked) */ #define SKIP_FIRST_TICK (1U << 0) int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor, int (*pred)(void *arg), void *pred_arg, uint32_t flags); + +/* + * ossl_quic_reactor_(enter/leave)_blocking_section + * ------------------------------------------------ + * + * This is used by blocking code outside of the reactor itself to inform the + * reactor of when a thread begins or ends a blocking call. This is used by the + * reactor so it knows if a tick means other threads might need to be woken up + * via the notifier. The reactor mutex must be held while calling these + * functions. + * + * The number of 'active' calls to these functions (i.e., the number of enter + * calls which have yet to be matched with a subsequent leave call) must *at all + * times* equal the number of threads blocking on the reactor. In other words, a + * single thread is not permitted to use these functions "recursively". Failing + * to adhere to this rule will result in deadlock. + * + * This means that if a caller has the concept of multiple concurrent blocking + * calls on the same thread on the same reactor (which may occur in some + * SSL_poll-related circumstances) it must do its own housekeeping to ensure it + * only calls enter() once. See quic_reactor_wait_ctx.h for a utility which can + * be used to accomplish this. + * + * ossl_quic_reactor_enter_blocking_section: + * Precondition: The current thread does not have an outstanding + * ossl_quic_reactor_enter_blocking_section() call (unchecked) + * Postcondition: The current thread has an outstanding + * ossl_quic_reactor_enter_blocking_section() call + * + * ossl_quic_reactor_leave_blocking_section: + * Precondition: The current thread has an outstanding + * ossl_quic_reactor_enter_blocking_section() call (unchecked) + * Postcondition: The current thread does not have an outstanding + * ossl_quic_reactor_enter_blocking_section() call + * + */ +void ossl_quic_reactor_enter_blocking_section(QUIC_REACTOR *rtor); +void ossl_quic_reactor_leave_blocking_section(QUIC_REACTOR *rtor); + # endif #endif diff --git a/ssl/quic/quic_reactor.c b/ssl/quic/quic_reactor.c index ed867998e8a..e7e7f58f6b6 100644 --- a/ssl/quic/quic_reactor.c +++ b/ssl/quic/quic_reactor.c @@ -487,7 +487,7 @@ int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor, /* Can't wait if there is nothing to wait for. */ return 0; - ++rtor->cur_blocking_waiters; + ossl_quic_reactor_enter_blocking_section(rtor); res = poll_two_descriptors(ossl_quic_reactor_get_poll_r(rtor), net_read_desired, @@ -497,9 +497,6 @@ int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor, tick_deadline, rtor->mutex); - assert(rtor->cur_blocking_waiters > 0); - --rtor->cur_blocking_waiters; - /* * We have now exited the OS poller call. We may have * (rtor->signalled_notifier), and other threads may still be blocking. @@ -540,22 +537,7 @@ int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor, * threads wait for this CV to be signalled. * */ - if (rtor->have_notifier && rtor->signalled_notifier) { - if (rtor->cur_blocking_waiters == 0) { - ossl_rio_notifier_unsignal(&rtor->notifier); - rtor->signalled_notifier = 0; - - /* - * Release the other threads which have woken up (and possibly - * rtor_notify_other_threads as well). - */ - ossl_crypto_condvar_broadcast(rtor->notifier_cv); - } else { - /* We are not the last waiter out - so wait for that one. */ - while (rtor->signalled_notifier) - ossl_crypto_condvar_wait(rtor->notifier_cv, rtor->mutex); - } - } + ossl_quic_reactor_leave_blocking_section(rtor); if (!res) /* @@ -578,3 +560,31 @@ int ossl_quic_reactor_block_until_pred(QUIC_REACTOR *rtor, return res; } + +void ossl_quic_reactor_enter_blocking_section(QUIC_REACTOR *rtor) +{ + ++rtor->cur_blocking_waiters; +} + +void ossl_quic_reactor_leave_blocking_section(QUIC_REACTOR *rtor) +{ + assert(rtor->cur_blocking_waiters > 0); + --rtor->cur_blocking_waiters; + + if (rtor->have_notifier && rtor->signalled_notifier) { + if (rtor->cur_blocking_waiters == 0) { + ossl_rio_notifier_unsignal(&rtor->notifier); + rtor->signalled_notifier = 0; + + /* + * Release the other threads which have woken up (and possibly + * rtor_notify_other_threads as well). + */ + ossl_crypto_condvar_broadcast(rtor->notifier_cv); + } else { + /* We are not the last waiter out - so wait for that one. */ + while (rtor->signalled_notifier) + ossl_crypto_condvar_wait(rtor->notifier_cv, rtor->mutex); + } + } +}