Index: tests/signal_handler/user_signal_test.cc |
diff --git a/tests/signal_handler/user_signal_test.cc b/tests/signal_handler/user_signal_test.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d3c9a285058eb30e04cfa2afd1e16f02de8376da |
--- /dev/null |
+++ b/tests/signal_handler/user_signal_test.cc |
@@ -0,0 +1,203 @@ |
+/* |
+ * Copyright (c) 2015 The Native Client Authors. All rights reserved. |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include <pthread.h> |
+#include <semaphore.h> |
+ |
+#include "native_client/src/include/nacl/nacl_signal.h" |
+#include "native_client/src/include/nacl_assert.h" |
+#include "native_client/src/public/nonsfi/irt_signal_handling.h" |
+#include "native_client/src/untrusted/irt/irt.h" |
+#include "native_client/src/untrusted/nacl/nacl_irt.h" |
+#include "native_client/src/untrusted/nacl/nacl_thread.h" |
+#include "native_client/src/untrusted/pthread/pthread_types.h" |
+ |
+volatile int g_signal_count; |
+volatile int g_futex; |
+volatile int g_test_running; |
+int g_tid; |
+void* g_expected_tls; |
+sem_t g_sem; |
+ |
+/* |
+ * Check that sending a signal before initializing signal support will result in |
+ * an error. |
+ */ |
+void test_send_signal_before_init() { |
+ int retval = nacl_signal_send_async_signal(0); |
+ ASSERT_EQ(retval, ESRCH); |
+} |
+ |
+/* |
+ * Check that nacl_tls_get() is signal-async-safe. |
Mark Seaborn
2015/06/26 18:32:00
Nit: "async-signal-safe"
Luis Héctor Chávez
2015/07/06 23:45:00
Done.
|
+ */ |
+void tls_get_signal_handler(NaClExceptionContext *exc) { |
+ if (!g_test_running) |
+ return; |
+ ASSERT_EQ(nacl_tls_get(), g_expected_tls); |
+ g_signal_count++; |
+ sem_post(&g_sem); |
Mark Seaborn
2015/06/26 18:32:00
Nit: check return value on all calls (sem_post(),
Luis Héctor Chávez
2015/07/06 23:45:00
Done.
|
+} |
+ |
+void *tls_get_thread_func(void *arg) { |
+ g_expected_tls = nacl_tls_get(); |
+ sem_post(&g_sem); |
+ while (g_test_running) { |
+ ASSERT_EQ(nacl_tls_get(), g_expected_tls); |
+ } |
+ return NULL; |
+} |
+ |
+void test_async_safe_tls_get() { |
+ nacl_signal_set_handler(tls_get_signal_handler); |
+ |
+ pthread_t tid; |
+ g_signal_count = 0; |
+ g_test_running = true; |
+ int result = pthread_create(&tid, NULL, tls_get_thread_func, NULL); |
+ ASSERT_EQ(0, result); |
+ |
+ sem_wait(&g_sem); |
+ const int kSignalCount = 1000; |
+ for (int i = 0; i < kSignalCount; i++) { |
+ nacl_signal_send_async_signal(tid->native_tid); |
Mark Seaborn
2015/06/26 18:32:00
Using tid->native_tid here is unfortunate because
Luis Héctor Chávez
2015/07/06 23:45:00
The trick was not needed, since I need to be able
|
+ sem_wait(&g_sem); |
+ } |
+ g_test_running = false; |
+ // Send a last signal to make sure any waiting syscalls get interrupted. |
+ nacl_signal_send_async_signal(tid->native_tid); |
+ pthread_join(tid, NULL); |
+ ASSERT_EQ(g_signal_count, kSignalCount); |
+} |
+ |
+/* |
+ * Check that both futex_wake() and futex_wait_abs() are signal-async-safe. |
+ */ |
+void futex_signal_handler(NaClExceptionContext *exc) { |
+ if (!g_test_running) |
+ return; |
+ g_signal_count++; |
+ sem_post(&g_sem); |
+ int count = 0; |
+ int retval = __libnacl_irt_futex.futex_wake(&g_futex, 1, &count); |
+ ASSERT_EQ(retval, 0); |
+ ASSERT_EQ(count, 0); |
+} |
+ |
+void *futex_thread_func(void *arg) { |
+ sem_post(&g_sem); |
+ while (g_test_running) { |
+ int retval = __libnacl_irt_futex.futex_wait_abs(&g_futex, 0, NULL); |
+ ASSERT_EQ(retval, EINTR); |
+ } |
+ return NULL; |
+} |
+ |
+void test_async_safe_futex() { |
+ nacl_signal_set_handler(futex_signal_handler); |
+ |
+ pthread_t tid; |
+ g_test_running = true; |
+ g_futex = 0; |
+ int result = pthread_create(&tid, NULL, futex_thread_func, NULL); |
+ ASSERT_EQ(0, result); |
+ |
+ sem_wait(&g_sem); |
+ g_signal_count = 0; |
+ const int kSignalCount = 1000; |
+ for (int i = 0; i < kSignalCount; i++) { |
+ nacl_signal_send_async_signal(tid->native_tid); |
+ sem_wait(&g_sem); |
+ } |
+ g_test_running = false; |
+ // Send a last signal to make sure any waiting syscalls get interrupted. |
+ nacl_signal_send_async_signal(tid->native_tid); |
+ pthread_join(tid, NULL); |
+ ASSERT_EQ(g_signal_count, kSignalCount); |
+} |
+ |
+/* |
+ * Check that nacl_signal_send_async_signal() is async-signal-safe. |
+ */ |
+void signal_signal_handler(NaClExceptionContext *exc) { |
+ if (!g_test_running) |
+ return; |
+ if (++g_signal_count % 2 == 1) { |
+ nacl_signal_send_async_signal(g_tid); |
+ sem_post(&g_sem); |
+ } |
+} |
+ |
+void *signal_thread_func(void *arg) { |
+ sem_post(&g_sem); |
+ while (g_test_running) { |
+ int retval = sleep(1); |
+ ASSERT_EQ(retval, 1); |
+ ASSERT_EQ(errno, EINTR); |
+ } |
+ return NULL; |
+} |
+ |
+void test_async_safe_signal() { |
+ nacl_signal_set_handler(signal_signal_handler); |
+ |
+ pthread_t tid; |
+ g_test_running = true; |
+ g_signal_count = 0; |
+ int result = pthread_create(&tid, NULL, signal_thread_func, NULL); |
+ ASSERT_EQ(0, result); |
+ g_tid = tid->native_tid; |
+ |
+ sem_wait(&g_sem); |
+ const int kSignalCount = 1000; |
+ for (int i = 0; i < kSignalCount; i++) { |
+ nacl_signal_send_async_signal(g_tid); |
+ sem_wait(&g_sem); |
+ } |
+ g_test_running = false; |
+ // Send a last signal to make sure any waiting syscalls get interrupted. |
+ nacl_signal_send_async_signal(g_tid); |
+ pthread_join(tid, NULL); |
+ ASSERT_EQ(g_signal_count, 2 * kSignalCount); |
+} |
+ |
+/* |
+ * Check that passing 0 as |tid| to nacl_signal_send_async_signal() works and |
+ * sends a signal to the main thread. |
+ */ |
+void main_signal_handler(NaClExceptionContext *exc) { |
+ g_signal_count = 1; |
+} |
+ |
+void test_main_signal() { |
+ nacl_signal_set_handler(main_signal_handler); |
+ |
+ g_signal_count = 0; |
+ int retval = nacl_signal_send_async_signal(0); |
+ ASSERT_EQ(retval, 0); |
+ ASSERT_EQ(g_signal_count, 1); |
+} |
+ |
+void run_test(const char *test_name, void (*test_func)(void)) { |
+ printf("Running %s...\n", test_name); |
+ test_func(); |
+} |
+ |
+#define RUN_TEST(test_func) (run_test(#test_func, test_func)) |
+ |
+int main(void) { |
+ RUN_TEST(test_send_signal_before_init); |
+ |
+ nonsfi_initialize_signal_handler(); |
+ sem_init(&g_sem, 0, 0); |
+ |
+ RUN_TEST(test_async_safe_tls_get); |
+ RUN_TEST(test_async_safe_futex); |
+ RUN_TEST(test_async_safe_signal); |
+ RUN_TEST(test_main_signal); |
+ |
+ return 0; |
+} |