1 : /*
2 : * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 : /*
7 : * A stand-alone application that tests thread local storage (TLS) on the
8 : * current platform. It is written to avoid locks and condition variables.
9 : * Since it is testing thread-specific stuff, it's good to use as little
10 : * *other* thread-specific stuff as possible for these tests.
11 : *
12 : * Note that this test hangs in pthread_join() on ARM QEMU.
13 : */
14 :
15 : #if NACL_LINUX || NACL_OSX
16 : #include <pthread.h>
17 : #endif
18 : #include <stdio.h>
19 : #include <stdlib.h>
20 : #include <string.h>
21 :
22 : #include "native_client/src/include/portability.h"
23 : #include "native_client/src/shared/platform/nacl_log.h"
24 : #include "native_client/src/shared/platform/nacl_time.h"
25 :
26 : /* Constants. */
27 : static const int NSEC_PER_MSEC = 1000 * 1000;
28 :
29 : /* A regular global variable, shared among all threads. */
30 : volatile int g_running_thread_count;
31 :
32 : /*
33 : * The TLS to be tested.
34 : * This variable is used in only one function. Since we're testing that the
35 : * memory is actually different for each thread, we need to make sure the
36 : * compiler does not optimize out the store/load of this variable or
37 : * rearrange when the store happens. As such, make the TLS variable
38 : * "volatile".
39 : */
40 : THREAD volatile int tls_data;
41 :
42 : /* Parameter block used when starting a thread. */
43 : struct ThreadData {
44 : int thread_num;
45 : int total_num_threads;
46 : int *p_tls_data_return;
47 : };
48 :
49 : /* Initializes NaCl modules. */
50 1 : static void Init(void) {
51 1 : const char *nacl_verbosity = getenv("NACLVERBOSITY");
52 :
53 1 : NaClLogModuleInit();
54 : NaClLogSetVerbosity((NULL == nacl_verbosity)
55 : ? 0
56 1 : : strtol(nacl_verbosity, (char **) 0, 0));
57 1 : NaClTimeInit();
58 1 : }
59 :
60 : /* Shuts down NaCl modules. */
61 1 : static void Fini(void) {
62 1 : NaClTimeFini();
63 1 : NaClLogModuleFini();
64 1 : }
65 :
66 : /* Prints an error message and exits with "failure" status. */
67 0 : static void ErrorExit(void) {
68 0 : NaClLog(LOG_ERROR, "TEST FAILED\n");
69 0 : Fini();
70 0 : exit(1);
71 : }
72 :
73 : /*
74 : * Waits for g_running_thread_count to exactly equal final_thread_count.
75 : * Returns true if final_thread_count reached, false if timed out.
76 : */
77 1 : static int WaitForThreads(int final_thread_count) {
78 1 : const int SLEEP_NSEC = 10 * NSEC_PER_MSEC; /* 10ms */
79 1 : const int SLEEP_ITERATIONS = 1000; /* *10ms = 10 sec */
80 :
81 : int i;
82 : struct nacl_abi_timespec sleep_time;
83 :
84 1 : sleep_time.tv_sec = 0;
85 1 : sleep_time.tv_nsec = SLEEP_NSEC;
86 :
87 1 : for (i = 0; i < SLEEP_ITERATIONS; ++i) {
88 1 : if (final_thread_count == g_running_thread_count) {
89 1 : return 1;
90 : }
91 1 : NaClNanosleep(&sleep_time, NULL);
92 1 : }
93 0 : return 0;
94 1 : }
95 :
96 : /* Executes the test of TLS for each thread. */
97 : #if NACL_LINUX || NACL_OSX
98 : typedef void *thread_param_t;
99 : typedef void *thread_return_t;
100 : #define OS_API_TYPE
101 : #elif NACL_WINDOWS
102 : typedef LPVOID thread_param_t;
103 : typedef DWORD thread_return_t;
104 : #define OS_API_TYPE WINAPI
105 : #endif
106 1 : static thread_return_t OS_API_TYPE ThreadEntryPoint(thread_param_t p) {
107 1 : struct ThreadData *param = (struct ThreadData *) p;
108 1 : int my_thread_num = param->thread_num;
109 :
110 : /* Set our TLS to our expected value. */
111 1 : tls_data = my_thread_num * 2;
112 :
113 : /* Tell the main thread that we are running. */
114 1 : ++g_running_thread_count;
115 :
116 : /* Wait for all threads to be running. */
117 1 : WaitForThreads(param->total_num_threads);
118 :
119 : /*
120 : * All of the threads have started and have written to their own TLS.
121 : * Read our TLS and give it back to the main thread.
122 : */
123 1 : param->p_tls_data_return[my_thread_num] = tls_data;
124 :
125 1 : return 0;
126 1 : }
127 :
128 : #if NACL_LINUX || NACL_OSX
129 : /*
130 : * Creates a thread on Linux.
131 : * Returns 0 for success.
132 : */
133 : static int MyCreateThread(pthread_t *p_pthread, void *p) {
134 : return pthread_create(p_pthread, NULL, ThreadEntryPoint, p);
135 : }
136 :
137 : /*
138 : * Waits for a thread to finish on Linux.
139 : * Returns 0 for success.
140 : */
141 : static int MyWaitForThreadExit(pthread_t pthread) {
142 : return pthread_join(pthread, NULL);
143 : }
144 :
145 : #elif NACL_WINDOWS
146 : /*
147 : * Creates a thread on Windows.
148 : * Returns 0 for success.
149 : */
150 1 : static int MyCreateThread(HANDLE *p_handle, void *p) {
151 : *p_handle = CreateThread(NULL, /* security. */
152 : 0, /* <64K stack size. */
153 : ThreadEntryPoint,
154 : p, /* Thread data. */
155 : 0, /* Thread runs immediately. */
156 1 : NULL); /* OS version of thread id. */
157 1 : return (NULL != *p_handle) ? 0 : 1;
158 1 : }
159 :
160 : /*
161 : * Waits for a thread to finish on Windows.
162 : * Returns 0 for success.
163 : */
164 1 : static int MyWaitForThreadExit(HANDLE handle) {
165 : static const DWORD MAX_WAIT_MSEC = 5000;
166 :
167 : DWORD result = WaitForMultipleObjects(1, /* Thread count. */
168 : &handle, /* Thread handle. */
169 : TRUE, /* Wait for all. */
170 1 : MAX_WAIT_MSEC); /* Max wait time */
171 1 : return (WAIT_OBJECT_0 == result) ? 0 : 1;
172 1 : }
173 : #endif
174 :
175 : /* Tests that threads can access TLS. */
176 1 : int main(void) {
177 : #define NUM_THREADS (10)
178 :
179 : int i;
180 : int rc;
181 : #if NACL_LINUX || NACL_OSX
182 : pthread_t thread_id[NUM_THREADS];
183 : #elif NACL_WINDOWS
184 : HANDLE thread_id[NUM_THREADS];
185 : #endif
186 : int tls_data_return[NUM_THREADS];
187 : struct ThreadData param;
188 : int test_passed;
189 :
190 : /* Initialize NaCl. */
191 1 : Init();
192 :
193 : /* Initialize global variables. */
194 1 : g_running_thread_count = 0;
195 :
196 : /* Initialize the per-thread data. */
197 1 : memset(thread_id, 0x00, sizeof(thread_id));
198 1 : memset(tls_data_return, 0xff, sizeof(tls_data_return));
199 :
200 : /* Initialize the thread parameter block. */
201 1 : param.total_num_threads = NUM_THREADS;
202 1 : param.p_tls_data_return = tls_data_return;
203 :
204 : /* Start each thread and have it set its TLS. */
205 1 : for (i = 0; i < NUM_THREADS; ++i) {
206 1 : param.thread_num = i;
207 1 : rc = MyCreateThread(&thread_id[i], ¶m);
208 1 : if (0 != rc) {
209 0 : NaClLog(LOG_ERROR, "ERROR: Could not create thread %d, rc=%d.\n", i, rc);
210 0 : ErrorExit();
211 : }
212 1 : if (!WaitForThreads(i + 1)) {
213 0 : NaClLog(LOG_ERROR, "ERROR: Timed out waiting for thread %d.\n", i);
214 0 : ErrorExit();
215 : }
216 1 : }
217 :
218 : /*
219 : * All threads have started and have written to their TLS. As soon as the
220 : * last thread was ready, all of the threads should have continued
221 : * executing and exited. Wait for each thread to exit, then validate its
222 : * TLS data.
223 : */
224 1 : test_passed = 1;
225 1 : for (i = 0; i < NUM_THREADS; ++i) {
226 1 : int expected = i * 2;
227 :
228 : /*
229 : * Print the expected and actual data to the console. Don't abort
230 : * immediately on error. This allows each thread's TLS to be examined so
231 : * that all of the bad data gets printed to help show a failure pattern.
232 : */
233 1 : rc = MyWaitForThreadExit(thread_id[i]);
234 1 : if (0 != rc) {
235 0 : NaClLog(LOG_ERROR, "ERROR: Thread %d did not exit, rc=%d.\n", i, rc);
236 0 : test_passed = 0;
237 1 : } else if (expected != tls_data_return[i]) {
238 : NaClLog(LOG_ERROR, "ERROR: Thread %d TLS data, expected=%d, actual=%d.\n",
239 0 : i, expected, tls_data_return[i]);
240 0 : test_passed = 0;
241 0 : } else {
242 : NaClLog(LOG_INFO, "OK: Thread %d TLS data, expected=%d, actual=%d.\n",
243 1 : i, expected, tls_data_return[i]);
244 : }
245 1 : }
246 1 : if (!test_passed) {
247 0 : ErrorExit();
248 : }
249 :
250 1 : NaClLog(LOG_INFO, "TEST PASSED\n");
251 1 : Fini();
252 1 : return 0;
253 1 : }
|