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