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. */
assist_thread_main(void * arg)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
ossl_quic_thread_assist_init_start(QUIC_THREAD_ASSIST * qta,QUIC_CHANNEL * ch)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
ossl_quic_thread_assist_stop_async(QUIC_THREAD_ASSIST * qta)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
ossl_quic_thread_assist_wait_stopped(QUIC_THREAD_ASSIST * qta)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
ossl_quic_thread_assist_cleanup(QUIC_THREAD_ASSIST * qta)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
ossl_quic_thread_assist_notify_deadline_changed(QUIC_THREAD_ASSIST * qta)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