1 /* Copyright (c) 2015, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15 #include "internal.h"
16
17 #include <stdio.h>
18
19
20 #if !defined(OPENSSL_NO_THREADS)
21
22 #if defined(OPENSSL_WINDOWS)
23
24 OPENSSL_MSVC_PRAGMA(warning(push, 3))
25 #include <windows.h>
26 OPENSSL_MSVC_PRAGMA(warning(pop))
27
28 typedef HANDLE thread_t;
29
thread_run(LPVOID arg)30 static DWORD WINAPI thread_run(LPVOID arg) {
31 void (*thread_func)(void);
32 /* VC really doesn't like casting between data and function pointers. */
33 OPENSSL_memcpy(&thread_func, &arg, sizeof(thread_func));
34 thread_func();
35 return 0;
36 }
37
run_thread(thread_t * out_thread,void (* thread_func)(void))38 static int run_thread(thread_t *out_thread, void (*thread_func)(void)) {
39 void *arg;
40 /* VC really doesn't like casting between data and function pointers. */
41 OPENSSL_memcpy(&arg, &thread_func, sizeof(arg));
42
43 *out_thread = CreateThread(NULL /* security attributes */,
44 0 /* default stack size */, thread_run, arg,
45 0 /* run immediately */, NULL /* ignore id */);
46 return *out_thread != NULL;
47 }
48
wait_for_thread(thread_t thread)49 static int wait_for_thread(thread_t thread) {
50 return WaitForSingleObject(thread, INFINITE) == 0;
51 }
52
53 #else
54
55 #include <pthread.h>
56 #include <string.h>
57 #include <time.h>
58
59 typedef pthread_t thread_t;
60
thread_run(void * arg)61 static void *thread_run(void *arg) {
62 void (*thread_func)(void) = arg;
63 thread_func();
64 return NULL;
65 }
66
run_thread(thread_t * out_thread,void (* thread_func)(void))67 static int run_thread(thread_t *out_thread, void (*thread_func)(void)) {
68 return pthread_create(out_thread, NULL /* default attributes */, thread_run,
69 thread_func) == 0;
70 }
71
wait_for_thread(thread_t thread)72 static int wait_for_thread(thread_t thread) {
73 return pthread_join(thread, NULL) == 0;
74 }
75
76 #endif /* OPENSSL_WINDOWS */
77
78 static unsigned g_once_init_called = 0;
79
once_init(void)80 static void once_init(void) {
81 g_once_init_called++;
82
83 /* Sleep briefly so one |call_once_thread| instance will call |CRYPTO_once|
84 * while the other is running this function. */
85 #if defined(OPENSSL_WINDOWS)
86 Sleep(1 /* milliseconds */);
87 #else
88 struct timespec req;
89 OPENSSL_memset(&req, 0, sizeof(req));
90 req.tv_nsec = 1000000;
91 nanosleep(&req, NULL);
92 #endif
93 }
94
95 static CRYPTO_once_t g_test_once = CRYPTO_ONCE_INIT;
96
call_once_thread(void)97 static void call_once_thread(void) {
98 CRYPTO_once(&g_test_once, once_init);
99 }
100
test_once(void)101 static int test_once(void) {
102 if (g_once_init_called != 0) {
103 fprintf(stderr, "g_once_init_called was non-zero at start.\n");
104 return 0;
105 }
106
107 thread_t thread1, thread2;
108 if (!run_thread(&thread1, call_once_thread) ||
109 !run_thread(&thread2, call_once_thread) ||
110 !wait_for_thread(thread1) ||
111 !wait_for_thread(thread2)) {
112 fprintf(stderr, "thread failed.\n");
113 return 0;
114 }
115
116 CRYPTO_once(&g_test_once, once_init);
117
118 if (g_once_init_called != 1) {
119 fprintf(stderr, "Expected init function to be called once, but found %u.\n",
120 g_once_init_called);
121 return 0;
122 }
123
124 return 1;
125 }
126
127
128 static int g_test_thread_ok = 0;
129 static unsigned g_destructor_called_count = 0;
130
thread_local_destructor(void * arg)131 static void thread_local_destructor(void *arg) {
132 if (arg == NULL) {
133 return;
134 }
135
136 unsigned *count = arg;
137 (*count)++;
138 }
139
thread_local_test_thread(void)140 static void thread_local_test_thread(void) {
141 void *ptr = CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_TEST);
142 if (ptr != NULL) {
143 return;
144 }
145
146 if (!CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_TEST,
147 &g_destructor_called_count,
148 thread_local_destructor)) {
149 return;
150 }
151
152 if (CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_TEST) !=
153 &g_destructor_called_count) {
154 return;
155 }
156
157 g_test_thread_ok = 1;
158 }
159
thread_local_test2_thread(void)160 static void thread_local_test2_thread(void) {}
161
test_thread_local(void)162 static int test_thread_local(void) {
163 void *ptr = CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_TEST);
164 if (ptr != NULL) {
165 fprintf(stderr, "Thread-local data was non-NULL at start.\n");
166 }
167
168 thread_t thread;
169 if (!run_thread(&thread, thread_local_test_thread) ||
170 !wait_for_thread(thread)) {
171 fprintf(stderr, "thread failed.\n");
172 return 0;
173 }
174
175 if (!g_test_thread_ok) {
176 fprintf(stderr, "Thread-local data didn't work in thread.\n");
177 return 0;
178 }
179
180 if (g_destructor_called_count != 1) {
181 fprintf(stderr,
182 "Destructor should have been called once, but actually called %u "
183 "times.\n",
184 g_destructor_called_count);
185 return 0;
186 }
187
188 /* thread_local_test2_thread doesn't do anything, but it tests that the
189 * thread destructor function works even if thread-local storage wasn't used
190 * for a thread. */
191 if (!run_thread(&thread, thread_local_test2_thread) ||
192 !wait_for_thread(thread)) {
193 fprintf(stderr, "thread failed.\n");
194 return 0;
195 }
196
197 return 1;
198 }
199
main(int argc,char ** argv)200 int main(int argc, char **argv) {
201 if (!test_once() ||
202 !test_thread_local()) {
203 return 1;
204 }
205
206 printf("PASS\n");
207 return 0;
208 }
209
210 #else /* OPENSSL_NO_THREADS */
211
main(int argc,char ** argv)212 int main(int argc, char **argv) {
213 printf("PASS\n");
214 return 0;
215 }
216
217 #endif
218