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

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

Issue 1212613002: Non-SFI mode: Add Linux asynchronous signal support (Closed) Base URL: https://chromium.googlesource.com/native_client/src/native_client.git@master
Patch Set: Rebased change for trybots 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
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 } // 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 }
OLDNEW
« documentation/nonsfi_mode_async_signals.txt ('K') | « tests/nonsfi/nacl.scons ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698