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