blob: c702ace24ba3541e6533376489e7b6920962f70d [file] [log] [blame]
Adam Langleyd7c5dfb2015-03-16 12:48:56 -07001/* 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
Adam Langley65a7e942015-05-07 18:28:27 -070020#if !defined(OPENSSL_NO_THREADS)
21
Adam Langleyd7c5dfb2015-03-16 12:48:56 -070022#if defined(OPENSSL_WINDOWS)
23
David Benjamina353cdb2016-06-09 16:48:33 -040024OPENSSL_MSVC_PRAGMA(warning(push, 3))
Adam Langley2b23eaa2015-06-09 11:50:45 -070025#include <windows.h>
David Benjamina353cdb2016-06-09 16:48:33 -040026OPENSSL_MSVC_PRAGMA(warning(pop))
Adam Langleyd7c5dfb2015-03-16 12:48:56 -070027
28typedef HANDLE thread_t;
29
30static DWORD WINAPI thread_run(LPVOID arg) {
31 void (*thread_func)(void);
32 /* VC really doesn't like casting between data and function pointers. */
David Benjamin17cf2cb2016-12-13 01:07:13 -050033 OPENSSL_memcpy(&thread_func, &arg, sizeof(thread_func));
Adam Langleyd7c5dfb2015-03-16 12:48:56 -070034 thread_func();
35 return 0;
36}
37
38static 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. */
David Benjamin17cf2cb2016-12-13 01:07:13 -050041 OPENSSL_memcpy(&arg, &thread_func, sizeof(arg));
Adam Langleyd7c5dfb2015-03-16 12:48:56 -070042
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
49static int wait_for_thread(thread_t thread) {
50 return WaitForSingleObject(thread, INFINITE) == 0;
51}
52
53#else
54
55#include <pthread.h>
David Benjaminf3835832016-04-19 17:48:11 -040056#include <string.h>
57#include <time.h>
Adam Langleyd7c5dfb2015-03-16 12:48:56 -070058
59typedef pthread_t thread_t;
60
61static void *thread_run(void *arg) {
62 void (*thread_func)(void) = arg;
63 thread_func();
64 return NULL;
65}
66
67static 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
72static int wait_for_thread(thread_t thread) {
73 return pthread_join(thread, NULL) == 0;
74}
75
76#endif /* OPENSSL_WINDOWS */
77
78static unsigned g_once_init_called = 0;
79
80static void once_init(void) {
81 g_once_init_called++;
David Benjamin3ed24f02016-03-30 19:03:45 -040082
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
David Benjaminf3835832016-04-19 17:48:11 -040088 struct timespec req;
David Benjamin17cf2cb2016-12-13 01:07:13 -050089 OPENSSL_memset(&req, 0, sizeof(req));
David Benjaminf3835832016-04-19 17:48:11 -040090 req.tv_nsec = 1000000;
91 nanosleep(&req, NULL);
David Benjamin3ed24f02016-03-30 19:03:45 -040092#endif
Adam Langleyd7c5dfb2015-03-16 12:48:56 -070093}
94
95static CRYPTO_once_t g_test_once = CRYPTO_ONCE_INIT;
96
97static void call_once_thread(void) {
98 CRYPTO_once(&g_test_once, once_init);
99}
100
101static 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
David Benjamin3ed24f02016-03-30 19:03:45 -0400107 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)) {
Adam Langleyd7c5dfb2015-03-16 12:48:56 -0700112 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
128static int g_test_thread_ok = 0;
129static unsigned g_destructor_called_count = 0;
130
131static void thread_local_destructor(void *arg) {
132 if (arg == NULL) {
133 return;
134 }
135
136 unsigned *count = arg;
137 (*count)++;
138}
139
140static 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
160static void thread_local_test2_thread(void) {}
161
162static 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
200int 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}
Adam Langley65a7e942015-05-07 18:28:27 -0700209
210#else /* OPENSSL_NO_THREADS */
211
212int main(int argc, char **argv) {
213 printf("PASS\n");
214 return 0;
215}
216
217#endif