OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright (c) 2015 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 #include <pthread.h> | |
8 #include <semaphore.h> | |
9 | |
10 #include "native_client/src/include/nacl_assert.h" | |
11 #include "native_client/src/untrusted/irt/irt.h" | |
12 #include "native_client/src/untrusted/nacl/nacl_irt.h" | |
13 #include "native_client/src/untrusted/nacl/nacl_thread.h" | |
14 | |
15 #define CHECK_OK(expr) ASSERT_EQ(expr, 0) | |
16 | |
17 namespace { | |
18 | |
19 struct nacl_irt_thread_v0_2 libnacl_irt_thread_v0_2; | |
20 struct nacl_irt_async_signal_handling libnacl_irt_async_signal_handling; | |
21 | |
22 volatile int g_signal_count; | |
23 volatile int g_signal_arrived; | |
24 volatile int g_test_running; | |
25 nacl_irt_tid_t g_child_tid; | |
26 void *g_expected_tls; | |
27 sem_t g_sem; | |
28 | |
29 } // namespace | |
Mark Seaborn
2015/08/13 21:17:50
Can you put all of the functions (except main()) i
Luis Héctor Chávez
2015/08/13 22:48:13
Done.
| |
30 | |
31 int thread_create_wrapper(void (*start_func)(void), void *stack, | |
32 void *thread_ptr) { | |
33 return libnacl_irt_thread_v0_2.thread_create(start_func, stack, thread_ptr, | |
34 &g_child_tid); | |
35 } | |
36 | |
37 int set_async_signal_handler(NaClIrtAsyncSignalHandler handler) { | |
38 return libnacl_irt_async_signal_handling.set_async_signal_handler(handler); | |
39 } | |
40 | |
41 int send_async_signal(nacl_irt_tid_t tid) { | |
42 return libnacl_irt_async_signal_handling.send_async_signal(tid); | |
43 } | |
44 | |
45 /* | |
46 * Check that sending a signal before initializing signal support will result in | |
47 * an error. | |
48 */ | |
49 void test_send_signal_before_set_handler() { | |
50 int retval = send_async_signal(0); | |
51 ASSERT_EQ(retval, ESRCH); | |
52 } | |
53 | |
54 /* | |
55 * Check that nacl_tls_get() is async-signal-safe. | |
56 */ | |
57 void tls_get_signal_handler(NaClExceptionContext *exc) { | |
58 if (!g_test_running) | |
59 return; | |
60 ASSERT_EQ(nacl_tls_get(), g_expected_tls); | |
61 g_signal_count++; | |
62 g_signal_arrived = 1; | |
63 } | |
64 | |
65 void *tls_get_thread_func(void *arg) { | |
66 g_expected_tls = nacl_tls_get(); | |
67 CHECK_OK(sem_post(&g_sem)); | |
68 while (g_test_running) { | |
69 ASSERT_EQ(nacl_tls_get(), g_expected_tls); | |
70 if (__sync_bool_compare_and_swap(&g_signal_arrived, 1, 0)) { | |
71 CHECK_OK(sem_post(&g_sem)); | |
72 } | |
73 } | |
74 return NULL; | |
75 } | |
76 | |
77 void test_async_safe_tls_get() { | |
78 CHECK_OK(sem_init(&g_sem, 0, 0)); | |
79 CHECK_OK(set_async_signal_handler(tls_get_signal_handler)); | |
80 | |
81 pthread_t tid; | |
82 g_signal_count = 0; | |
83 g_signal_arrived = 0; | |
84 g_test_running = true; | |
85 CHECK_OK(pthread_create(&tid, NULL, tls_get_thread_func, NULL)); | |
86 | |
87 CHECK_OK(sem_wait(&g_sem)); | |
88 const int kSignalCount = 1000; | |
89 for (int i = 0; i < kSignalCount; i++) { | |
90 CHECK_OK(send_async_signal(g_child_tid)); | |
91 CHECK_OK(sem_wait(&g_sem)); | |
92 } | |
93 g_test_running = false; | |
94 /* Send a last signal to make sure any waiting syscalls get interrupted. */ | |
95 CHECK_OK(send_async_signal(g_child_tid)); | |
96 CHECK_OK(pthread_join(tid, NULL)); | |
97 ASSERT_EQ(g_signal_count, kSignalCount); | |
98 CHECK_OK(sem_destroy(&g_sem)); | |
99 } | |
100 | |
101 /* | |
102 * Check that both futex_wake() and futex_wait_abs() are signal-async-safe. | |
103 */ | |
104 void futex_signal_handler(NaClExceptionContext *exc) { | |
105 int count = 0; | |
106 ASSERT_EQ(__sync_bool_compare_and_swap(&g_signal_arrived, 0, 1), 1); | |
107 CHECK_OK(__libnacl_irt_futex.futex_wake(&g_signal_arrived, INT_MAX, &count)); | |
108 /* | |
109 * "count" is always 0 since the thread waiting is now running the signal | |
110 * handler, so it did not actually counts as a wakeup. | |
Mark Seaborn
2015/08/13 21:17:50
"so it did not actually count as a wakeup"
Luis Héctor Chávez
2015/08/13 22:48:13
Done.
| |
111 */ | |
112 ASSERT_EQ(count, 0); | |
113 if (g_test_running) | |
114 g_signal_count++; | |
115 } | |
116 | |
117 void *futex_thread_func(void *arg) { | |
118 CHECK_OK(sem_post(&g_sem)); | |
119 struct timespec timeout; | |
120 /* | |
121 * Make the timeout be the current time plus 10 seconds. This timeout should | |
122 * never kick in, but if it does it means we deadlocked, so it's better to | |
123 * assert than letting the job itself time out. | |
124 */ | |
125 clock_gettime(CLOCK_REALTIME, &timeout); | |
126 timeout.tv_sec += 10; | |
127 while (g_test_running) { | |
128 int retval = __libnacl_irt_futex.futex_wait_abs(&g_signal_arrived, 0, | |
129 &timeout); | |
130 if (retval == EWOULDBLOCK) { | |
131 /* | |
132 * The signal handler executed before we could wait and changed the value | |
133 * of |g_signal_arrived|. | |
134 */ | |
135 } else { | |
136 /* | |
137 * futex_wait_abs, when provided with a non-NULL timeout argument, can be | |
138 * interrupted and will set errno to EINTR. This can happen even if the | |
139 * SA_RESTART flag was used. | |
140 */ | |
141 ASSERT_EQ(retval, EINTR); | |
142 } | |
143 ASSERT_EQ(__sync_bool_compare_and_swap(&g_signal_arrived, 1, 0), 1); | |
144 /* | |
145 * Have to test again since we could have gone sleeping again after the last | |
146 * iteration. | |
147 */ | |
148 if (g_test_running) | |
149 CHECK_OK(sem_post(&g_sem)); | |
150 } | |
151 return NULL; | |
152 } | |
153 | |
154 void test_async_safe_futex() { | |
155 CHECK_OK(sem_init(&g_sem, 0, 0)); | |
156 CHECK_OK(set_async_signal_handler(futex_signal_handler)); | |
157 | |
158 pthread_t tid; | |
159 g_signal_count = 0; | |
160 g_signal_arrived = 0; | |
161 g_test_running = true; | |
162 CHECK_OK(pthread_create(&tid, NULL, futex_thread_func, NULL)); | |
163 | |
164 CHECK_OK(sem_wait(&g_sem)); | |
165 const int kSignalCount = 1000; | |
166 for (int i = 0; i < kSignalCount; i++) { | |
167 CHECK_OK(send_async_signal(g_child_tid)); | |
168 CHECK_OK(sem_wait(&g_sem)); | |
169 } | |
170 g_test_running = false; | |
171 /* Send a last signal to make sure any waiting syscalls get interrupted. */ | |
172 CHECK_OK(send_async_signal(g_child_tid)); | |
173 CHECK_OK(pthread_join(tid, NULL)); | |
174 ASSERT_EQ(g_signal_count, kSignalCount); | |
175 CHECK_OK(sem_destroy(&g_sem)); | |
176 } | |
177 | |
178 /* | |
179 * Check that send_async_signal() is async-signal-safe. | |
180 */ | |
181 void signal_signal_handler(NaClExceptionContext *exc) { | |
182 if (!g_test_running) | |
183 return; | |
184 if (++g_signal_count % 2 == 1) { | |
185 CHECK_OK(send_async_signal(g_child_tid)); | |
186 g_signal_arrived = 1; | |
187 } | |
188 } | |
189 | |
190 void *signal_thread_func(void *arg) { | |
191 CHECK_OK(sem_post(&g_sem)); | |
192 struct timespec req, rem; | |
193 /* | |
194 * In case we are unlucky and the signal arrives before the first sleep, limit | |
195 * the time sleeping to 10 msec. | |
196 */ | |
197 req.tv_sec = 0; | |
198 req.tv_nsec = 10000000; | |
199 while (g_test_running) { | |
200 while (g_test_running && !g_signal_arrived) { | |
201 int retval = nanosleep(&req, &rem); | |
202 if (retval != 0) | |
203 ASSERT_EQ(errno, EINTR); | |
204 } | |
205 /* | |
206 * Have to test again since we could have gone sleeping again after the last | |
207 * iteration. | |
208 */ | |
209 if (!g_test_running) | |
210 break; | |
211 g_signal_arrived = 0; | |
212 CHECK_OK(sem_post(&g_sem)); | |
213 } | |
214 return NULL; | |
215 } | |
216 | |
217 void test_async_safe_signal() { | |
218 CHECK_OK(sem_init(&g_sem, 0, 0)); | |
219 CHECK_OK(set_async_signal_handler(signal_signal_handler)); | |
220 | |
221 pthread_t tid; | |
222 g_test_running = true; | |
223 g_signal_count = 0; | |
224 g_signal_arrived = 0; | |
225 CHECK_OK(pthread_create(&tid, NULL, signal_thread_func, NULL)); | |
226 | |
227 CHECK_OK(sem_wait(&g_sem)); | |
228 const int kSignalCount = 1000; | |
229 for (int i = 0; i < kSignalCount; i++) { | |
230 CHECK_OK(send_async_signal(g_child_tid)); | |
231 CHECK_OK(sem_wait(&g_sem)); | |
232 } | |
233 g_test_running = false; | |
234 /* Send a last signal to make sure any waiting syscalls get interrupted. */ | |
235 CHECK_OK(send_async_signal(g_child_tid)); | |
236 CHECK_OK(pthread_join(tid, NULL)); | |
237 ASSERT_EQ(g_signal_count, 2 * kSignalCount); | |
238 CHECK_OK(sem_destroy(&g_sem)); | |
239 } | |
240 | |
241 /* | |
242 * Check that passing 0 as |tid| to send_async_signal() works and | |
243 * sends a signal to the main thread. | |
244 */ | |
245 void main_signal_handler(NaClExceptionContext *exc) { | |
246 g_signal_count = 1; | |
247 } | |
248 | |
249 void test_main_signal() { | |
250 CHECK_OK(set_async_signal_handler(main_signal_handler)); | |
251 | |
252 g_signal_count = 0; | |
253 CHECK_OK(send_async_signal(NACL_IRT_MAIN_THREAD_TID)); | |
254 ASSERT_EQ(g_signal_count, 1); | |
255 } | |
256 | |
257 void run_test(const char *test_name, void (*test_func)(void)) { | |
258 printf("Running %s...\n", test_name); | |
259 test_func(); | |
260 } | |
261 | |
262 #define RUN_TEST(test_func) (run_test(#test_func, test_func)) | |
263 | |
264 int main(void) { | |
265 size_t bytes; | |
266 bytes = nacl_interface_query(NACL_IRT_THREAD_v0_2, &libnacl_irt_thread_v0_2, | |
267 sizeof(libnacl_irt_thread_v0_2)); | |
268 ASSERT_EQ(bytes, sizeof(libnacl_irt_thread_v0_2)); | |
269 | |
270 bytes = nacl_interface_query(NACL_IRT_ASYNC_SIGNAL_HANDLING_v0_1, | |
271 &libnacl_irt_async_signal_handling, | |
272 sizeof(libnacl_irt_async_signal_handling)); | |
273 ASSERT_EQ(bytes, sizeof(libnacl_irt_async_signal_handling)); | |
274 | |
275 /* | |
276 * In order to avoid modifying the libpthread implementation to save the | |
277 * native tid, wrap that functionality so the tid is stored in a global | |
278 * variable. | |
279 */ | |
280 __libnacl_irt_thread.thread_create = &thread_create_wrapper; | |
281 | |
282 RUN_TEST(test_send_signal_before_set_handler); | |
283 | |
284 RUN_TEST(test_async_safe_tls_get); | |
285 #if !defined(__arm__) | |
286 /* | |
287 * Signals are sometimes delivered after the futex_wait syscall returns (as | |
288 * opposed to interrupting it), which breaks this test. | |
289 * | |
290 * This problem only seems to happen in QEMU. | |
291 */ | |
292 RUN_TEST(test_async_safe_futex); | |
293 #endif | |
294 RUN_TEST(test_async_safe_signal); | |
295 RUN_TEST(test_main_signal); | |
296 | |
297 printf("Done\n"); | |
298 | |
299 return 0; | |
300 } | |
OLD | NEW |