xref: /openssl/test/threadpool_test.c (revision 7ed6de99)
1 /*
2  * Copyright 2022-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 <string.h>
11 #include <internal/cryptlib.h>
12 #include <internal/thread_arch.h>
13 #include <internal/thread.h>
14 #include <openssl/thread.h>
15 #include "testutil.h"
16 
test_thread_reported_flags(void)17 static int test_thread_reported_flags(void)
18 {
19     uint32_t flags = OSSL_get_thread_support_flags();
20 
21 #if !defined(OPENSSL_THREADS)
22     if (!TEST_int_eq(flags, 0))
23         return 0;
24 #endif
25 
26 #if defined(OPENSSL_NO_THREAD_POOL)
27     if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL, 0))
28         return 0;
29 #else
30     if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL,
31                      OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL))
32         return 0;
33 #endif
34 
35 #if defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
36     if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN, 0))
37         return 0;
38 #else
39     if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN,
40                      OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN))
41         return 0;
42 #endif
43 
44     return 1;
45 }
46 
47 #ifndef OPENSSL_NO_THREAD_POOL
48 
49 # define TEST_THREAD_NATIVE_FN_SET_VALUE 1
test_thread_native_fn(void * data)50 static uint32_t test_thread_native_fn(void *data)
51 {
52     uint32_t *ldata = (uint32_t*) data;
53     *ldata = *ldata + 1;
54     return *ldata - 1;
55 }
56 /* Tests of native threads */
57 
test_thread_native(void)58 static int test_thread_native(void)
59 {
60     uint32_t retval;
61     uint32_t local;
62     CRYPTO_THREAD *t;
63 
64     /* thread spawn, join */
65 
66     local = 1;
67     t = ossl_crypto_thread_native_start(test_thread_native_fn, &local, 1);
68     if (!TEST_ptr(t))
69         return 0;
70 
71     /*
72      * pthread_join results in undefined behaviour if called on a joined
73      * thread. We do not impose such restrictions, so it's up to us to
74      * ensure that this does not happen (thread sanitizer will warn us
75      * if we do).
76      */
77     if (!TEST_int_eq(ossl_crypto_thread_native_join(t, &retval), 1))
78         return 0;
79     if (!TEST_int_eq(ossl_crypto_thread_native_join(t, &retval), 1))
80         return 0;
81 
82     if (!TEST_int_eq(retval, 1) || !TEST_int_eq(local, 2))
83         return 0;
84 
85     if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 1))
86         return 0;
87     t = NULL;
88 
89     if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 0))
90         return 0;
91 
92     return 1;
93 }
94 
95 # if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
test_thread_internal(void)96 static int test_thread_internal(void)
97 {
98     uint32_t retval[3];
99     uint32_t local[3] = { 0 };
100     uint32_t threads_supported;
101     size_t i;
102     void *t[3];
103     int status = 0;
104     OSSL_LIB_CTX *cust_ctx = OSSL_LIB_CTX_new();
105 
106     threads_supported = OSSL_get_thread_support_flags();
107     threads_supported &= OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN;
108 
109     if (threads_supported == 0) {
110         if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
111             goto cleanup;
112         if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
113             goto cleanup;
114 
115         if (!TEST_int_eq(OSSL_set_max_threads(NULL, 1), 0))
116             goto cleanup;
117         if (!TEST_int_eq(OSSL_set_max_threads(cust_ctx, 1), 0))
118             goto cleanup;
119 
120         if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
121             goto cleanup;
122         if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
123             goto cleanup;
124 
125         t[0] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
126         if (!TEST_ptr_null(t[0]))
127             goto cleanup;
128 
129         status = 1;
130         goto cleanup;
131     }
132 
133     /* fail when not allowed to use threads */
134 
135     if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
136         goto cleanup;
137     t[0] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
138     if (!TEST_ptr_null(t[0]))
139         goto cleanup;
140 
141     /* fail when enabled on a different context */
142     if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
143         goto cleanup;
144     if (!TEST_int_eq(OSSL_set_max_threads(cust_ctx, 1), 1))
145         goto cleanup;
146     if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
147         goto cleanup;
148     if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 1))
149         goto cleanup;
150     t[0] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
151     if (!TEST_ptr_null(t[0]))
152         goto cleanup;
153     if (!TEST_int_eq(OSSL_set_max_threads(cust_ctx, 0), 1))
154         goto cleanup;
155 
156     /* sequential startup */
157 
158     if (!TEST_int_eq(OSSL_set_max_threads(NULL, 1), 1))
159         goto cleanup;
160     if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 1))
161         goto cleanup;
162     if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
163         goto cleanup;
164 
165     for (i = 0; i < OSSL_NELEM(t); ++i) {
166         local[0] = i + 1;
167 
168         t[i] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
169         if (!TEST_ptr(t[i]))
170             goto cleanup;
171 
172         /*
173          * pthread_join results in undefined behaviour if called on a joined
174          * thread. We do not impose such restrictions, so it's up to us to
175          * ensure that this does not happen (thread sanitizer will warn us
176          * if we do).
177          */
178         if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[0]), 1))
179             goto cleanup;
180         if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[0]), 1))
181             goto cleanup;
182 
183         if (!TEST_int_eq(retval[0], i + 1) || !TEST_int_eq(local[0], i + 2))
184             goto cleanup;
185 
186         if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 1))
187             goto cleanup;
188         t[i] = NULL;
189 
190         if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 0))
191             goto cleanup;
192     }
193 
194     /* parallel startup */
195 
196     if (!TEST_int_eq(OSSL_set_max_threads(NULL, OSSL_NELEM(t)), 1))
197         goto cleanup;
198 
199     for (i = 0; i < OSSL_NELEM(t); ++i) {
200         local[i] = i + 1;
201         t[i] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[i]);
202         if (!TEST_ptr(t[i]))
203             goto cleanup;
204     }
205     for (i = 0; i < OSSL_NELEM(t); ++i) {
206         if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[i]), 1))
207             goto cleanup;
208     }
209     for (i = 0; i < OSSL_NELEM(t); ++i) {
210         if (!TEST_int_eq(retval[i], i + 1) || !TEST_int_eq(local[i], i + 2))
211             goto cleanup;
212         if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 1))
213             goto cleanup;
214     }
215 
216     /* parallel startup, bottleneck */
217 
218     if (!TEST_int_eq(OSSL_set_max_threads(NULL, OSSL_NELEM(t) - 1), 1))
219         goto cleanup;
220 
221     for (i = 0; i < OSSL_NELEM(t); ++i) {
222         local[i] = i + 1;
223         t[i] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[i]);
224         if (!TEST_ptr(t[i]))
225             goto cleanup;
226     }
227     for (i = 0; i < OSSL_NELEM(t); ++i) {
228         if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[i]), 1))
229             goto cleanup;
230     }
231     for (i = 0; i < OSSL_NELEM(t); ++i) {
232         if (!TEST_int_eq(retval[i], i + 1) || !TEST_int_eq(local[i], i + 2))
233             goto cleanup;
234         if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 1))
235             goto cleanup;
236     }
237 
238     if (!TEST_int_eq(OSSL_set_max_threads(NULL, 0), 1))
239         goto cleanup;
240 
241     status = 1;
242 cleanup:
243     OSSL_LIB_CTX_free(cust_ctx);
244     return status;
245 }
246 # endif
247 
test_thread_native_multiple_joins_fn1(void * data)248 static uint32_t test_thread_native_multiple_joins_fn1(void *data)
249 {
250     return 0;
251 }
252 
test_thread_native_multiple_joins_fn2(void * data)253 static uint32_t test_thread_native_multiple_joins_fn2(void *data)
254 {
255     ossl_crypto_thread_native_join((CRYPTO_THREAD *)data, NULL);
256     return 0;
257 }
258 
test_thread_native_multiple_joins_fn3(void * data)259 static uint32_t test_thread_native_multiple_joins_fn3(void *data)
260 {
261     ossl_crypto_thread_native_join((CRYPTO_THREAD *)data, NULL);
262     return 0;
263 }
264 
test_thread_native_multiple_joins(void)265 static int test_thread_native_multiple_joins(void)
266 {
267     CRYPTO_THREAD *t, *t1, *t2;
268 
269     t = ossl_crypto_thread_native_start(test_thread_native_multiple_joins_fn1, NULL, 1);
270     t1 = ossl_crypto_thread_native_start(test_thread_native_multiple_joins_fn2, t, 1);
271     t2 = ossl_crypto_thread_native_start(test_thread_native_multiple_joins_fn3, t, 1);
272 
273     if (!TEST_ptr(t) || !TEST_ptr(t1) || !TEST_ptr(t2))
274         return 0;
275 
276     if (!TEST_int_eq(ossl_crypto_thread_native_join(t2, NULL), 1))
277         return 0;
278     if (!TEST_int_eq(ossl_crypto_thread_native_join(t1, NULL), 1))
279         return 0;
280 
281     if (!TEST_int_eq(ossl_crypto_thread_native_clean(t2), 1))
282         return 0;
283 
284     if (!TEST_int_eq(ossl_crypto_thread_native_clean(t1), 1))
285         return 0;
286 
287     if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 1))
288         return 0;
289 
290     return 1;
291 }
292 
293 #endif
294 
setup_tests(void)295 int setup_tests(void)
296 {
297     ADD_TEST(test_thread_reported_flags);
298 #if !defined(OPENSSL_NO_THREAD_POOL)
299     ADD_TEST(test_thread_native);
300     ADD_TEST(test_thread_native_multiple_joins);
301 # if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
302     ADD_TEST(test_thread_internal);
303 # endif
304 #endif
305 
306     return 1;
307 }
308