Implement new multi-threading API
[openssl.git] / test / threadstest.c
1 /* ====================================================================
2  * Copyright (c) 2016 The OpenSSL Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * 3. All advertising materials mentioning features or use of this
17  *    software must display the following acknowledgment:
18  *    "This product includes software developed by the OpenSSL Project
19  *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
20  *
21  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
22  *    endorse or promote products derived from this software without
23  *    prior written permission. For written permission, please contact
24  *    openssl-core@openssl.org.
25  *
26  * 5. Products derived from this software may not be called "OpenSSL"
27  *    nor may "OpenSSL" appear in their names without prior written
28  *    permission of the OpenSSL Project.
29  *
30  * 6. Redistributions of any form whatsoever must retain the following
31  *    acknowledgment:
32  *    "This product includes software developed by the OpenSSL Project
33  *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
36  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
41  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
46  * OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  */
49
50 #include <stdio.h>
51
52 #include <openssl/crypto.h>
53 #include "internal/threads.h"
54
55 #if !defined(OPENSSL_THREADS) || defined(CRYPTO_TDEBUG)
56
57 typedef unsigned int thread_t;
58
59 static int run_thread(thread_t *t, void (*f)(void))
60 {
61     f();
62     return 1;
63 }
64
65 static int wait_for_thread(thread_t thread)
66 {
67     return 1;
68 }
69
70 #elif defined(OPENSSL_SYS_WINDOWS)
71
72 typedef HANDLE thread_t;
73
74 static DWORD WINAPI thread_run(LPVOID arg)
75 {
76     void (*f)(void);
77
78     *(void **) (&f) = arg;
79
80     f();
81     return 0;
82 }
83
84 static int run_thread(thread_t *t, void (*f)(void))
85 {
86     *t = CreateThread(NULL, 0, thread_run, *(void **) &f, 0, NULL);
87     return *t != NULL;
88 }
89
90 static int wait_for_thread(thread_t thread)
91 {
92     return WaitForSingleObject(thread, INFINITE) == 0;
93 }
94
95 #else
96
97 typedef pthread_t thread_t;
98
99 static void *thread_run(void *arg)
100 {
101     void (*f)(void);
102
103     *(void **) (&f) = arg;
104
105     f();
106     return NULL;
107 }
108
109 static int run_thread(thread_t *t, void (*f)(void))
110 {
111     return pthread_create(t, NULL, thread_run, *(void **) &f) == 0;
112 }
113
114 static int wait_for_thread(thread_t thread)
115 {
116     return pthread_join(thread, NULL) == 0;
117 }
118
119 #endif
120
121 static int test_lock(void)
122 {
123     CRYPTO_RWLOCK *lock = CRYPTO_THREAD_lock_new();
124
125     if (!CRYPTO_THREAD_read_lock(lock)) {
126         fprintf(stderr, "CRYPTO_THREAD_read_lock() failed\n");
127         return 0;
128     }
129
130     if (!CRYPTO_THREAD_unlock(lock)) {
131         fprintf(stderr, "CRYPTO_THREAD_unlock() failed\n");
132         return 0;
133     }
134
135     CRYPTO_THREAD_lock_free(lock);
136
137     return 1;
138 }
139
140 static CRYPTO_ONCE once_run = CRYPTO_ONCE_STATIC_INIT;
141 static unsigned once_run_count = 0;
142
143 static void once_do_run(void)
144 {
145     once_run_count++;
146 }
147
148 static void once_run_thread_cb(void)
149 {
150     CRYPTO_THREAD_run_once(&once_run, once_do_run);
151 }
152
153 static int test_once(void)
154 {
155     thread_t thread;
156     if (!run_thread(&thread, once_run_thread_cb) ||
157         !wait_for_thread(thread))
158     {
159         fprintf(stderr, "run_thread() failed\n");
160         return 0;
161     }
162
163     if (!CRYPTO_THREAD_run_once(&once_run, once_do_run)) {
164         fprintf(stderr, "CRYPTO_THREAD_run_once() failed\n");
165         return 0;
166     }
167
168     if (once_run_count != 1) {
169         fprintf(stderr, "once run %u times\n", once_run_count);
170         return 0;
171     }
172
173     return 1;
174 }
175
176 static CRYPTO_THREAD_LOCAL thread_local_key;
177 static unsigned destructor_run_count = 0;
178 static int thread_local_thread_cb_ok = 0;
179
180 static void thread_local_destructor(void *arg)
181 {
182     unsigned *count;
183
184     if (arg == NULL)
185         return;
186
187     count = arg;
188
189     (*count)++;
190 }
191
192 static void thread_local_thread_cb(void)
193 {
194     void *ptr;
195
196     ptr = CRYPTO_THREAD_get_local(&thread_local_key);
197     if (ptr != NULL) {
198         fprintf(stderr, "ptr not NULL\n");
199         return;
200     }
201
202     if (!CRYPTO_THREAD_set_local(&thread_local_key, &destructor_run_count)) {
203         fprintf(stderr, "CRYPTO_THREAD_set_local() failed\n");
204         return;
205     }
206
207     ptr = CRYPTO_THREAD_get_local(&thread_local_key);
208     if (ptr != &destructor_run_count) {
209         fprintf(stderr, "invalid ptr\n");
210         return;
211     }
212
213     thread_local_thread_cb_ok = 1;
214 }
215
216 static int test_thread_local(void)
217 {
218     thread_t thread;
219     void *ptr = NULL;
220
221     if (!CRYPTO_THREAD_init_local(&thread_local_key, thread_local_destructor)) {
222         fprintf(stderr, "CRYPTO_THREAD_init_local() failed\n");
223         return 0;
224     }
225
226     ptr = CRYPTO_THREAD_get_local(&thread_local_key);
227     if (ptr != NULL) {
228         fprintf(stderr, "ptr not NULL\n");
229         return 0;
230     }
231
232     if (!run_thread(&thread, thread_local_thread_cb) ||
233         !wait_for_thread(thread))
234     {
235         fprintf(stderr, "run_thread() failed\n");
236         return 0;
237     }
238
239     if (thread_local_thread_cb_ok != 1) {
240         fprintf(stderr, "thread-local thread callback failed\n");
241         return 0;
242     }
243
244 #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
245
246     ptr = CRYPTO_THREAD_get_local(&thread_local_key);
247     if (ptr != NULL) {
248         fprintf(stderr, "ptr not NULL\n");
249         return 0;
250     }
251
252 # if !defined(OPENSSL_SYS_WINDOWS)
253     if (destructor_run_count != 1) {
254         fprintf(stderr, "thread-local destructor run %u times\n",
255                 destructor_run_count);
256         return 0;
257     }
258 # endif
259
260 #endif
261
262     if (!CRYPTO_THREAD_cleanup_local(&thread_local_key)) {
263         fprintf(stderr, "CRYPTO_THREAD_cleanup_local() failed\n");
264         return 0;
265     }
266
267     return 1;
268 }
269
270 int main(int argc, char **argv)
271 {
272     if (!test_lock())
273       return 1;
274
275     if (!test_once())
276       return 1;
277
278     if (!test_thread_local())
279       return 1;
280
281     printf("PASS\n");
282     return 0;
283 }