xref: /freebsd/crypto/openssl/ssl/quic/quic_thread_assist.c (revision e7be843b4a162e68651d3911f0357ed464915629)
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