1 /* 2 * Copyright 2023-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 10 #include <openssl/macros.h> 11 #include "quic_local.h" 12 #include "internal/time.h" 13 #include "internal/thread.h" 14 #include "internal/thread_arch.h" 15 #include "internal/quic_thread_assist.h" 16 17 #if !defined(OPENSSL_NO_QUIC_THREAD_ASSIST) 18 19 /* Main loop for the QUIC assist thread. */ 20 static unsigned int assist_thread_main(void *arg) 21 { 22 QUIC_THREAD_ASSIST *qta = arg; 23 CRYPTO_MUTEX *m = ossl_quic_channel_get_mutex(qta->ch); 24 QUIC_REACTOR *rtor; 25 QUIC_ENGINE *eng = ossl_quic_channel_get0_engine(qta->ch); 26 27 ossl_crypto_mutex_lock(m); 28 29 rtor = ossl_quic_channel_get_reactor(qta->ch); 30 31 for (;;) { 32 OSSL_TIME deadline; 33 34 if (qta->teardown) 35 break; 36 37 deadline = ossl_quic_reactor_get_tick_deadline(rtor); 38 /* 39 * ossl_crypto_condvar_wait_timeout needs to use real time for the 40 * deadline 41 */ 42 deadline = ossl_quic_engine_make_real_time(eng, deadline); 43 ossl_crypto_condvar_wait_timeout(qta->cv, m, deadline); 44 45 /* 46 * We have now been woken up. This can be for one of the following 47 * reasons: 48 * 49 * - We have been asked to teardown (qta->teardown is set); 50 * - The tick deadline has passed. 51 * - The tick deadline has changed. 52 * 53 * For robustness, this loop also handles spurious wakeups correctly 54 * (which does not require any extra code). 55 */ 56 if (qta->teardown) 57 break; 58 59 ossl_quic_reactor_tick(rtor, QUIC_REACTOR_TICK_FLAG_CHANNEL_ONLY); 60 } 61 62 ossl_crypto_mutex_unlock(m); 63 return 1; 64 } 65 66 int ossl_quic_thread_assist_init_start(QUIC_THREAD_ASSIST *qta, 67 QUIC_CHANNEL *ch) 68 { 69 CRYPTO_MUTEX *mutex = ossl_quic_channel_get_mutex(ch); 70 71 if (mutex == NULL) 72 return 0; 73 74 qta->ch = ch; 75 qta->teardown = 0; 76 qta->joined = 0; 77 78 qta->cv = ossl_crypto_condvar_new(); 79 if (qta->cv == NULL) 80 return 0; 81 82 qta->t = ossl_crypto_thread_native_start(assist_thread_main, 83 qta, /*joinable=*/1); 84 if (qta->t == NULL) { 85 ossl_crypto_condvar_free(&qta->cv); 86 return 0; 87 } 88 89 return 1; 90 } 91 92 int ossl_quic_thread_assist_stop_async(QUIC_THREAD_ASSIST *qta) 93 { 94 if (!qta->teardown) { 95 qta->teardown = 1; 96 ossl_crypto_condvar_signal(qta->cv); 97 } 98 99 return 1; 100 } 101 102 int ossl_quic_thread_assist_wait_stopped(QUIC_THREAD_ASSIST *qta) 103 { 104 CRYPTO_THREAD_RETVAL rv; 105 CRYPTO_MUTEX *m = ossl_quic_channel_get_mutex(qta->ch); 106 107 if (qta->joined) 108 return 1; 109 110 if (!ossl_quic_thread_assist_stop_async(qta)) 111 return 0; 112 113 ossl_crypto_mutex_unlock(m); 114 115 if (!ossl_crypto_thread_native_join(qta->t, &rv)) { 116 ossl_crypto_mutex_lock(m); 117 return 0; 118 } 119 120 qta->joined = 1; 121 122 ossl_crypto_mutex_lock(m); 123 return 1; 124 } 125 126 int ossl_quic_thread_assist_cleanup(QUIC_THREAD_ASSIST *qta) 127 { 128 if (!ossl_assert(qta->joined)) 129 return 0; 130 131 ossl_crypto_condvar_free(&qta->cv); 132 ossl_crypto_thread_native_clean(qta->t); 133 134 qta->ch = NULL; 135 qta->t = NULL; 136 return 1; 137 } 138 139 int ossl_quic_thread_assist_notify_deadline_changed(QUIC_THREAD_ASSIST *qta) 140 { 141 if (qta->teardown) 142 return 0; 143 144 ossl_crypto_condvar_signal(qta->cv); 145 return 1; 146 } 147 148 #endif 149