Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(136)

Side by Side Diff: tests/nonsfi/user_async_signal_test.cc

Issue 1293793009: Reland "Non-SFI mode: Add Linux asynchronous signal support" (Closed) Base URL: https://chromium.googlesource.com/native_client/src/native_client.git@master
Patch Set: Fixed races on thread termination and removed the broken FP-restoration code Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« src/untrusted/irt/irt.h ('K') | « tests/nonsfi/nacl.scons ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 int thread_create_wrapper(void (*start_func)(void), void *stack,
30 void *thread_ptr) {
31 return libnacl_irt_thread_v0_2.thread_create(start_func, stack, thread_ptr,
32 &g_child_tid);
33 }
34
35 int set_async_signal_handler(NaClIrtAsyncSignalHandler handler) {
36 return libnacl_irt_async_signal_handling.set_async_signal_handler(handler);
37 }
38
39 int send_async_signal(nacl_irt_tid_t tid) {
40 return libnacl_irt_async_signal_handling.send_async_signal(tid);
41 }
42
43 /*
44 * Check that sending a signal before initializing signal support will result in
45 * an error.
46 */
47 void test_send_signal_before_set_handler() {
48 int retval = send_async_signal(0);
49 ASSERT_EQ(retval, ESRCH);
50 }
51
52 /*
53 * Check that nacl_tls_get() is async-signal-safe.
54 */
55 void tls_get_signal_handler(NaClExceptionContext *exc) {
56 if (!g_test_running)
57 return;
58 ASSERT_EQ(nacl_tls_get(), g_expected_tls);
59 g_signal_count++;
60 g_signal_arrived = 1;
61 }
62
63 void *tls_get_thread_func(void *arg) {
64 g_expected_tls = nacl_tls_get();
65 CHECK_OK(sem_post(&g_sem));
66 while (g_test_running) {
67 ASSERT_EQ(nacl_tls_get(), g_expected_tls);
68 if (__sync_bool_compare_and_swap(&g_signal_arrived, 1, 0)) {
69 CHECK_OK(sem_post(&g_sem));
70 }
71 }
72 return NULL;
73 }
74
75 void test_async_safe_tls_get() {
76 CHECK_OK(sem_init(&g_sem, 0, 0));
77 CHECK_OK(set_async_signal_handler(tls_get_signal_handler));
78
79 pthread_t tid;
80 g_signal_count = 0;
81 g_signal_arrived = 0;
82 g_test_running = true;
83 CHECK_OK(pthread_create(&tid, NULL, tls_get_thread_func, NULL));
84
85 CHECK_OK(sem_wait(&g_sem));
86 const int kSignalCount = 1000;
87 for (int i = 0; i < kSignalCount; i++) {
88 CHECK_OK(send_async_signal(g_child_tid));
89 CHECK_OK(sem_wait(&g_sem));
90 }
91 g_test_running = false;
92 /* Send a last signal to make sure any waiting syscalls get interrupted. */
93 int retval = send_async_signal(g_child_tid);
94 if (retval != 0) {
95 /* Thread might have exited before we sent the signal. */
Mark Seaborn 2015/08/20 15:44:30 This is problematic, because in documentation/nons
Luis Héctor Chávez 2015/08/20 16:13:49 I think that the doc change is enough. Upstream us
96 ASSERT_EQ(retval, ESRCH);
97 }
98 CHECK_OK(pthread_join(tid, NULL));
99 ASSERT_EQ(g_signal_count, kSignalCount);
100 CHECK_OK(sem_destroy(&g_sem));
101 }
102
103 #if !defined(__arm__)
104 /* This test is broken on QEMU. */
105
106 /*
107 * Check that both futex_wake() and futex_wait_abs() are signal-async-safe.
108 */
109 void futex_signal_handler(NaClExceptionContext *exc) {
110 int count = 0;
111 ASSERT_EQ(__sync_bool_compare_and_swap(&g_signal_arrived, 0, 1), 1);
112 CHECK_OK(__libnacl_irt_futex.futex_wake(&g_signal_arrived, INT_MAX, &count));
113 /*
114 * |count| is always 0 since the thread waiting is now running the signal
115 * handler, so it did not actually count as a wakeup.
116 */
117 ASSERT_EQ(count, 0);
118 if (g_test_running)
119 g_signal_count++;
120 }
121
122 void *futex_thread_func(void *arg) {
123 CHECK_OK(sem_post(&g_sem));
124 struct timespec timeout;
125 /*
126 * Make the timeout be the current time plus 10 seconds. This timeout should
127 * never kick in, but if it does it means we deadlocked, so it's better to
128 * assert than letting the job itself time out.
129 */
130 clock_gettime(CLOCK_REALTIME, &timeout);
131 timeout.tv_sec += 10;
132 while (g_test_running) {
133 int retval = __libnacl_irt_futex.futex_wait_abs(&g_signal_arrived, 0,
134 &timeout);
135 if (retval == EWOULDBLOCK) {
136 /*
137 * The signal handler executed before we could wait and changed the value
138 * of |g_signal_arrived|.
139 */
140 } else {
141 /*
142 * futex_wait_abs, when provided with a non-NULL timeout argument, can be
143 * interrupted and will set errno to EINTR. This can happen even if the
144 * SA_RESTART flag was used.
145 */
146 ASSERT_EQ(retval, EINTR);
147 }
148 ASSERT_EQ(__sync_bool_compare_and_swap(&g_signal_arrived, 1, 0), 1);
149 /*
150 * Have to test again since we could have gone sleeping again after the last
151 * iteration.
152 */
153 if (g_test_running)
154 CHECK_OK(sem_post(&g_sem));
155 }
156 return NULL;
157 }
158
159 void test_async_safe_futex() {
160 CHECK_OK(sem_init(&g_sem, 0, 0));
161 CHECK_OK(set_async_signal_handler(futex_signal_handler));
162
163 pthread_t tid;
164 g_signal_count = 0;
165 g_signal_arrived = 0;
166 g_test_running = true;
167 CHECK_OK(pthread_create(&tid, NULL, futex_thread_func, NULL));
168
169 CHECK_OK(sem_wait(&g_sem));
170 const int kSignalCount = 1000;
171 for (int i = 0; i < kSignalCount; i++) {
172 CHECK_OK(send_async_signal(g_child_tid));
173 CHECK_OK(sem_wait(&g_sem));
174 }
175 g_test_running = false;
176 /* Send a last signal to make sure any waiting syscalls get interrupted. */
Mark Seaborn 2015/08/20 15:44:30 How about splitting these 6 lines into a separate
Luis Héctor Chávez 2015/08/20 16:13:49 Done.
177 int retval = send_async_signal(g_child_tid);
178 if (retval != 0) {
179 /* Thread might have exited before we sent the signal. */
180 ASSERT_EQ(retval, ESRCH);
181 }
182 CHECK_OK(pthread_join(tid, NULL));
183 ASSERT_EQ(g_signal_count, kSignalCount);
184 CHECK_OK(sem_destroy(&g_sem));
185 }
186
187 #endif
188
189 /*
190 * Check that futex_wait_abs() with no timeout is restarted.
191 * As opposed to the above test with futex, the signal handler does not try to
192 * wake the thread up, since it will sometimes be called _after_ the
193 * futex_wait_abs() returns.
194 */
195 void futex_wait_signal_handler(NaClExceptionContext *exc) {
196 ASSERT_EQ(__sync_bool_compare_and_swap(&g_signal_arrived, 0, 1), 1);
197 }
198
199 void *futex_wait_thread_func(void *arg) {
200 volatile int *futex = (volatile int *)arg;
201 CHECK_OK(sem_post(&g_sem));
202 while (g_test_running) {
203 /*
204 * Unfortunately, Linux sometimes can return 0 (instead of EINTR) on
205 * futex_wait_abs() when it is spuriously woken up.
206 */
207 while (*futex == 0) {
208 int retval = __libnacl_irt_futex.futex_wait_abs(futex, 0, NULL);
209 if (retval != EWOULDBLOCK)
210 ASSERT_EQ(retval, 0);
211 }
212 ASSERT_EQ(__sync_bool_compare_and_swap(futex, 1, 0), 1);
213
214 /*
215 * Have to test again since we could have gone sleeping again after the last
216 * iteration.
217 */
218 if (g_test_running) {
219 ASSERT_EQ(__sync_bool_compare_and_swap(&g_signal_arrived, 1, 0), 1);
220 g_signal_count++;
221 CHECK_OK(sem_post(&g_sem));
222 }
223 }
224 return NULL;
225 }
226
227 void test_futex_wait_restart() {
228 CHECK_OK(sem_init(&g_sem, 0, 0));
229 CHECK_OK(set_async_signal_handler(futex_wait_signal_handler));
230
231 pthread_t tid;
232 g_signal_count = 0;
233 g_signal_arrived = 0;
234 volatile int futex = 0;
235 g_test_running = true;
236 CHECK_OK(pthread_create(&tid, NULL, futex_wait_thread_func, (void *)&futex));
237
238 CHECK_OK(sem_wait(&g_sem));
239 const int kSignalCount = 1000;
240 int count = 0;
241 for (int i = 0; i < kSignalCount; i++) {
242 /* Yield to the other process to try and get it in the desired state. */
243 sched_yield();
244 CHECK_OK(send_async_signal(g_child_tid));
245 sched_yield();
246
247 /* Wake it up using futex. This time, |count| may be 1. */
248 ASSERT_EQ(__sync_bool_compare_and_swap(&futex, 0, 1), 1);
249 CHECK_OK(__libnacl_irt_futex.futex_wake(&futex, INT_MAX, &count));
250 ASSERT_LE(count, 1);
251
252 CHECK_OK(sem_wait(&g_sem));
253 }
254 g_test_running = false;
255 /*
256 * Wake the thread up again in case it waited again.
257 */
258 __sync_bool_compare_and_swap(&futex, 0, 1);
259 CHECK_OK(__libnacl_irt_futex.futex_wake(&futex, INT_MAX, &count));
260 CHECK_OK(pthread_join(tid, NULL));
261 ASSERT_EQ(g_signal_count, kSignalCount);
262 CHECK_OK(sem_destroy(&g_sem));
263 }
264
265 /*
266 * Check that send_async_signal() is async-signal-safe.
267 */
268 void signal_signal_handler(NaClExceptionContext *exc) {
269 if (!g_test_running)
270 return;
271 if (++g_signal_count % 2 == 1) {
272 CHECK_OK(send_async_signal(g_child_tid));
273 g_signal_arrived = 1;
274 }
275 }
276
277 void *signal_thread_func(void *arg) {
278 CHECK_OK(sem_post(&g_sem));
279 struct timespec req, rem;
280 /*
281 * In case we are unlucky and the signal arrives before the first sleep, limit
282 * the time sleeping to 10 msec.
283 */
284 req.tv_sec = 0;
285 req.tv_nsec = 10000000;
286 while (g_test_running) {
287 while (g_test_running && !g_signal_arrived) {
288 int retval = nanosleep(&req, &rem);
289 if (retval != 0)
290 ASSERT_EQ(errno, EINTR);
291 }
292 /*
293 * Have to test again since we could have gone sleeping again after the last
294 * iteration.
295 */
296 if (!g_test_running)
297 break;
298 g_signal_arrived = 0;
299 CHECK_OK(sem_post(&g_sem));
300 }
301 return NULL;
302 }
303
304 void test_async_safe_signal() {
305 CHECK_OK(sem_init(&g_sem, 0, 0));
306 CHECK_OK(set_async_signal_handler(signal_signal_handler));
307
308 pthread_t tid;
309 g_test_running = true;
310 g_signal_count = 0;
311 g_signal_arrived = 0;
312 CHECK_OK(pthread_create(&tid, NULL, signal_thread_func, NULL));
313
314 CHECK_OK(sem_wait(&g_sem));
315 const int kSignalCount = 1000;
316 for (int i = 0; i < kSignalCount; i++) {
317 CHECK_OK(send_async_signal(g_child_tid));
318 CHECK_OK(sem_wait(&g_sem));
319 }
320 g_test_running = false;
321 /* Send a last signal to make sure any waiting syscalls get interrupted. */
322 int retval = send_async_signal(g_child_tid);
323 if (retval != 0) {
324 /* Thread might have exited before we sent the signal. */
325 ASSERT_EQ(retval, ESRCH);
326 }
327 CHECK_OK(pthread_join(tid, NULL));
328 ASSERT_EQ(g_signal_count, 2 * kSignalCount);
329 CHECK_OK(sem_destroy(&g_sem));
330 }
331
332 /*
333 * Check that passing 0 as |tid| to send_async_signal() works and
334 * sends a signal to the main thread.
335 */
336 void main_signal_handler(NaClExceptionContext *exc) {
337 g_signal_count = 1;
338 }
339
340 void test_main_signal() {
341 CHECK_OK(set_async_signal_handler(main_signal_handler));
342
343 g_signal_count = 0;
344 CHECK_OK(send_async_signal(NACL_IRT_MAIN_THREAD_TID));
345 ASSERT_EQ(g_signal_count, 1);
346 }
347
348 void run_test(const char *test_name, void (*test_func)(void)) {
349 printf("Running %s...\n", test_name);
350 test_func();
351 }
352
353 } // namespace
354
355 #define RUN_TEST(test_func) (run_test(#test_func, test_func))
356
357 int main(void) {
358 size_t bytes;
359 bytes = nacl_interface_query(NACL_IRT_THREAD_v0_2, &libnacl_irt_thread_v0_2,
360 sizeof(libnacl_irt_thread_v0_2));
361 ASSERT_EQ(bytes, sizeof(libnacl_irt_thread_v0_2));
362
363 bytes = nacl_interface_query(NACL_IRT_ASYNC_SIGNAL_HANDLING_v0_1,
364 &libnacl_irt_async_signal_handling,
365 sizeof(libnacl_irt_async_signal_handling));
366 ASSERT_EQ(bytes, sizeof(libnacl_irt_async_signal_handling));
367
368 /*
369 * In order to avoid modifying the libpthread implementation to save the
370 * native tid, wrap that functionality so the tid is stored in a global
371 * variable.
372 */
373 __libnacl_irt_thread.thread_create = &thread_create_wrapper;
374
375 RUN_TEST(test_send_signal_before_set_handler);
376
377 RUN_TEST(test_async_safe_tls_get);
378 #if !defined(__arm__)
379 /*
380 * Signals are sometimes delivered after the futex_wait syscall returns (as
381 * opposed to interrupting it), which breaks this test.
382 *
383 * This problem only seems to happen in QEMU.
384 */
385 RUN_TEST(test_async_safe_futex);
386 #endif
387 RUN_TEST(test_futex_wait_restart);
388 RUN_TEST(test_async_safe_signal);
389 RUN_TEST(test_main_signal);
390
391 printf("Done\n");
392
393 return 0;
394 }
OLDNEW
« src/untrusted/irt/irt.h ('K') | « tests/nonsfi/nacl.scons ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698