]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC REACTOR: Add support for external registration of blocking operations
authorHugo Landau <hlandau@openssl.org>
Mon, 13 May 2024 19:20:23 +0000 (20:20 +0100)
committerNeil Horman <nhorman@openssl.org>
Mon, 17 Feb 2025 16:27:33 +0000 (11:27 -0500)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Saša Nedvědický <sashan@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25416)

include/internal/quic_reactor.h
ssl/quic/quic_reactor.c

index bc5e5bd5f643d9aebd2e2667f8ca04e20b9adab7..7dadb8b83de8538e3057b48f981d5b8b6dfb71b8 100644 (file)
@@ -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
index ed867998e8afee2241b356faaad4613d6d230040..e7e7f58f6b673df829236e860cb878a70a073942 100644 (file)
@@ -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);
+        }
+    }
+}