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

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

Powered by Google App Engine
This is Rietveld 408576698