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

Unified 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 side-by-side diff with in-line comments
Download patch
« documentation/nonsfi_mode_async_signals.txt ('K') | « tests/nonsfi/nacl.scons ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tests/nonsfi/user_async_signal_test.cc
diff --git a/tests/nonsfi/user_async_signal_test.cc b/tests/nonsfi/user_async_signal_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5960421b6a18c41b44bf00e6b853f60fb6c0eef6
--- /dev/null
+++ b/tests/nonsfi/user_async_signal_test.cc
@@ -0,0 +1,300 @@
+/*
+ * 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_assert.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"
+
+#define CHECK_OK(expr) ASSERT_EQ(expr, 0)
+
+namespace {
+
+struct nacl_irt_thread_v0_2 libnacl_irt_thread_v0_2;
+struct nacl_irt_async_signal_handling libnacl_irt_async_signal_handling;
+
+volatile int g_signal_count;
+volatile int g_signal_arrived;
+volatile int g_test_running;
+nacl_irt_tid_t g_child_tid;
+void *g_expected_tls;
+sem_t g_sem;
+
+} // 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.
+
+int thread_create_wrapper(void (*start_func)(void), void *stack,
+ void *thread_ptr) {
+ return libnacl_irt_thread_v0_2.thread_create(start_func, stack, thread_ptr,
+ &g_child_tid);
+}
+
+int set_async_signal_handler(NaClIrtAsyncSignalHandler handler) {
+ return libnacl_irt_async_signal_handling.set_async_signal_handler(handler);
+}
+
+int send_async_signal(nacl_irt_tid_t tid) {
+ return libnacl_irt_async_signal_handling.send_async_signal(tid);
+}
+
+/*
+ * Check that sending a signal before initializing signal support will result in
+ * an error.
+ */
+void test_send_signal_before_set_handler() {
+ int retval = send_async_signal(0);
+ ASSERT_EQ(retval, ESRCH);
+}
+
+/*
+ * Check that nacl_tls_get() is async-signal-safe.
+ */
+void tls_get_signal_handler(NaClExceptionContext *exc) {
+ if (!g_test_running)
+ return;
+ ASSERT_EQ(nacl_tls_get(), g_expected_tls);
+ g_signal_count++;
+ g_signal_arrived = 1;
+}
+
+void *tls_get_thread_func(void *arg) {
+ g_expected_tls = nacl_tls_get();
+ CHECK_OK(sem_post(&g_sem));
+ while (g_test_running) {
+ ASSERT_EQ(nacl_tls_get(), g_expected_tls);
+ if (__sync_bool_compare_and_swap(&g_signal_arrived, 1, 0)) {
+ CHECK_OK(sem_post(&g_sem));
+ }
+ }
+ return NULL;
+}
+
+void test_async_safe_tls_get() {
+ CHECK_OK(sem_init(&g_sem, 0, 0));
+ CHECK_OK(set_async_signal_handler(tls_get_signal_handler));
+
+ pthread_t tid;
+ g_signal_count = 0;
+ g_signal_arrived = 0;
+ g_test_running = true;
+ CHECK_OK(pthread_create(&tid, NULL, tls_get_thread_func, NULL));
+
+ CHECK_OK(sem_wait(&g_sem));
+ const int kSignalCount = 1000;
+ for (int i = 0; i < kSignalCount; i++) {
+ CHECK_OK(send_async_signal(g_child_tid));
+ CHECK_OK(sem_wait(&g_sem));
+ }
+ g_test_running = false;
+ /* Send a last signal to make sure any waiting syscalls get interrupted. */
+ CHECK_OK(send_async_signal(g_child_tid));
+ CHECK_OK(pthread_join(tid, NULL));
+ ASSERT_EQ(g_signal_count, kSignalCount);
+ CHECK_OK(sem_destroy(&g_sem));
+}
+
+/*
+ * Check that both futex_wake() and futex_wait_abs() are signal-async-safe.
+ */
+void futex_signal_handler(NaClExceptionContext *exc) {
+ int count = 0;
+ ASSERT_EQ(__sync_bool_compare_and_swap(&g_signal_arrived, 0, 1), 1);
+ CHECK_OK(__libnacl_irt_futex.futex_wake(&g_signal_arrived, INT_MAX, &count));
+ /*
+ * "count" is always 0 since the thread waiting is now running the signal
+ * 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.
+ */
+ ASSERT_EQ(count, 0);
+ if (g_test_running)
+ g_signal_count++;
+}
+
+void *futex_thread_func(void *arg) {
+ CHECK_OK(sem_post(&g_sem));
+ struct timespec timeout;
+ /*
+ * Make the timeout be the current time plus 10 seconds. This timeout should
+ * never kick in, but if it does it means we deadlocked, so it's better to
+ * assert than letting the job itself time out.
+ */
+ clock_gettime(CLOCK_REALTIME, &timeout);
+ timeout.tv_sec += 10;
+ while (g_test_running) {
+ int retval = __libnacl_irt_futex.futex_wait_abs(&g_signal_arrived, 0,
+ &timeout);
+ if (retval == EWOULDBLOCK) {
+ /*
+ * The signal handler executed before we could wait and changed the value
+ * of |g_signal_arrived|.
+ */
+ } else {
+ /*
+ * futex_wait_abs, when provided with a non-NULL timeout argument, can be
+ * interrupted and will set errno to EINTR. This can happen even if the
+ * SA_RESTART flag was used.
+ */
+ ASSERT_EQ(retval, EINTR);
+ }
+ ASSERT_EQ(__sync_bool_compare_and_swap(&g_signal_arrived, 1, 0), 1);
+ /*
+ * Have to test again since we could have gone sleeping again after the last
+ * iteration.
+ */
+ if (g_test_running)
+ CHECK_OK(sem_post(&g_sem));
+ }
+ return NULL;
+}
+
+void test_async_safe_futex() {
+ CHECK_OK(sem_init(&g_sem, 0, 0));
+ CHECK_OK(set_async_signal_handler(futex_signal_handler));
+
+ pthread_t tid;
+ g_signal_count = 0;
+ g_signal_arrived = 0;
+ g_test_running = true;
+ CHECK_OK(pthread_create(&tid, NULL, futex_thread_func, NULL));
+
+ CHECK_OK(sem_wait(&g_sem));
+ const int kSignalCount = 1000;
+ for (int i = 0; i < kSignalCount; i++) {
+ CHECK_OK(send_async_signal(g_child_tid));
+ CHECK_OK(sem_wait(&g_sem));
+ }
+ g_test_running = false;
+ /* Send a last signal to make sure any waiting syscalls get interrupted. */
+ CHECK_OK(send_async_signal(g_child_tid));
+ CHECK_OK(pthread_join(tid, NULL));
+ ASSERT_EQ(g_signal_count, kSignalCount);
+ CHECK_OK(sem_destroy(&g_sem));
+}
+
+/*
+ * Check that send_async_signal() is async-signal-safe.
+ */
+void signal_signal_handler(NaClExceptionContext *exc) {
+ if (!g_test_running)
+ return;
+ if (++g_signal_count % 2 == 1) {
+ CHECK_OK(send_async_signal(g_child_tid));
+ g_signal_arrived = 1;
+ }
+}
+
+void *signal_thread_func(void *arg) {
+ CHECK_OK(sem_post(&g_sem));
+ struct timespec req, rem;
+ /*
+ * In case we are unlucky and the signal arrives before the first sleep, limit
+ * the time sleeping to 10 msec.
+ */
+ req.tv_sec = 0;
+ req.tv_nsec = 10000000;
+ while (g_test_running) {
+ while (g_test_running && !g_signal_arrived) {
+ int retval = nanosleep(&req, &rem);
+ if (retval != 0)
+ ASSERT_EQ(errno, EINTR);
+ }
+ /*
+ * Have to test again since we could have gone sleeping again after the last
+ * iteration.
+ */
+ if (!g_test_running)
+ break;
+ g_signal_arrived = 0;
+ CHECK_OK(sem_post(&g_sem));
+ }
+ return NULL;
+}
+
+void test_async_safe_signal() {
+ CHECK_OK(sem_init(&g_sem, 0, 0));
+ CHECK_OK(set_async_signal_handler(signal_signal_handler));
+
+ pthread_t tid;
+ g_test_running = true;
+ g_signal_count = 0;
+ g_signal_arrived = 0;
+ CHECK_OK(pthread_create(&tid, NULL, signal_thread_func, NULL));
+
+ CHECK_OK(sem_wait(&g_sem));
+ const int kSignalCount = 1000;
+ for (int i = 0; i < kSignalCount; i++) {
+ CHECK_OK(send_async_signal(g_child_tid));
+ CHECK_OK(sem_wait(&g_sem));
+ }
+ g_test_running = false;
+ /* Send a last signal to make sure any waiting syscalls get interrupted. */
+ CHECK_OK(send_async_signal(g_child_tid));
+ CHECK_OK(pthread_join(tid, NULL));
+ ASSERT_EQ(g_signal_count, 2 * kSignalCount);
+ CHECK_OK(sem_destroy(&g_sem));
+}
+
+/*
+ * Check that passing 0 as |tid| to 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() {
+ CHECK_OK(set_async_signal_handler(main_signal_handler));
+
+ g_signal_count = 0;
+ CHECK_OK(send_async_signal(NACL_IRT_MAIN_THREAD_TID));
+ 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) {
+ size_t bytes;
+ bytes = nacl_interface_query(NACL_IRT_THREAD_v0_2, &libnacl_irt_thread_v0_2,
+ sizeof(libnacl_irt_thread_v0_2));
+ ASSERT_EQ(bytes, sizeof(libnacl_irt_thread_v0_2));
+
+ bytes = nacl_interface_query(NACL_IRT_ASYNC_SIGNAL_HANDLING_v0_1,
+ &libnacl_irt_async_signal_handling,
+ sizeof(libnacl_irt_async_signal_handling));
+ ASSERT_EQ(bytes, sizeof(libnacl_irt_async_signal_handling));
+
+ /*
+ * In order to avoid modifying the libpthread implementation to save the
+ * native tid, wrap that functionality so the tid is stored in a global
+ * variable.
+ */
+ __libnacl_irt_thread.thread_create = &thread_create_wrapper;
+
+ RUN_TEST(test_send_signal_before_set_handler);
+
+ RUN_TEST(test_async_safe_tls_get);
+#if !defined(__arm__)
+ /*
+ * Signals are sometimes delivered after the futex_wait syscall returns (as
+ * opposed to interrupting it), which breaks this test.
+ *
+ * This problem only seems to happen in QEMU.
+ */
+ RUN_TEST(test_async_safe_futex);
+#endif
+ RUN_TEST(test_async_safe_signal);
+ RUN_TEST(test_main_signal);
+
+ printf("Done\n");
+
+ return 0;
+}
« 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