summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Guenthe <guenther@cvs.openbsd.org>2011-11-18 05:30:46 +0000
committerPhilip Guenthe <guenther@cvs.openbsd.org>2011-11-18 05:30:46 +0000
commite1e017930a787c7ded2b25344d51c70924de2814 (patch)
tree1446f52f2d49c88e3543417cd303a7a6e7e2f81c
parent02f627af3b990547e5f7d6fd233a2f9069a8e32f (diff)
Regress test for per-(r)thread errno address
-rw-r--r--regress/lib/libpthread/errno/Makefile5
-rw-r--r--regress/lib/libpthread/errno/errno.c196
2 files changed, 201 insertions, 0 deletions
diff --git a/regress/lib/libpthread/errno/Makefile b/regress/lib/libpthread/errno/Makefile
new file mode 100644
index 00000000000..0ecd65c68ec
--- /dev/null
+++ b/regress/lib/libpthread/errno/Makefile
@@ -0,0 +1,5 @@
+# $OpenBSD: Makefile,v 1.1 2011/11/18 05:30:45 guenther Exp $
+
+PROG= errno
+
+.include <bsd.regress.mk>
diff --git a/regress/lib/libpthread/errno/errno.c b/regress/lib/libpthread/errno/errno.c
new file mode 100644
index 00000000000..ff62f346854
--- /dev/null
+++ b/regress/lib/libpthread/errno/errno.c
@@ -0,0 +1,196 @@
+/* $OpenBSD: errno.c,v 1.1 2011/11/18 05:30:45 guenther Exp $ */
+/* PUBLIC DOMAIN Sep 2011 <guenther@openbsd.org> */
+
+/*
+ * Verify that &errno is different for each thread and is stable across
+ * context switches and in signal handlers
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "test.h"
+
+int *main_errno, *t1_errno, *t2_errno, **handler_errno;
+pthread_t main_tid, t1_tid, t2_tid;
+
+enum state
+{
+ START,
+ T1_START,
+ T1_SIGNAL,
+ T1_CHECK2,
+ T1_EXIT,
+} state;
+
+pthread_mutex_t m;
+pthread_cond_t c;
+sigset_t sigusr2;
+
+static void
+set_state(enum state new_state)
+{
+ CHECKe(pthread_mutex_lock(&m));
+ ASSERT(state == new_state - 1);
+ state = new_state;
+ CHECKe(pthread_cond_signal(&c));
+ CHECKe(pthread_mutex_unlock(&m));
+}
+
+static void
+wait_for_state(enum state new_state)
+{
+ CHECKe(pthread_mutex_lock(&m));
+ while(state != new_state)
+ CHECKe(pthread_cond_wait(&c, &m));
+ CHECKe(pthread_mutex_unlock(&m));
+}
+
+
+/*
+ * Yes, pthread_self() isn't async-signal-safe in general, but it should
+ * be okay for the regress test here
+ */
+static void
+act_handler(int signal)
+{
+ ASSERT(signal == SIGUSR1);
+ if (handler_errno == &main_errno) {
+ CHECKe(write(STDOUT_FILENO, "m", 1));
+ ASSERT(&errno == main_errno);
+ ASSERTe(errno, == EXDEV);
+ ASSERT(pthread_equal(t1_tid, pthread_self()));
+ } else if (handler_errno == &t1_errno) {
+ CHECKe(write(STDOUT_FILENO, "1", 1));
+ ASSERT(&errno == t1_errno);
+ ASSERTe(errno, == EXDEV);
+ ASSERT(pthread_equal(t1_tid, pthread_self()));
+ errno = ENODEV;
+ CHECKe(kill(getpid(), SIGUSR2));
+ ASSERTe(errno, == ENODEV);
+ } else if (handler_errno == &t2_errno) {
+ CHECKe(write(STDOUT_FILENO, "2", 1));
+ ASSERT(&errno == t2_errno);
+ ASSERTe(errno, == EXDEV);
+ ASSERT(pthread_equal(t2_tid, pthread_self()));
+ } else {
+ PANIC("unknown thread in act_handler!");
+ }
+}
+
+void *
+tmain(void *arg)
+{
+ t1_errno = &errno;
+ ASSERT(t1_errno != main_errno);
+ ASSERT(*t1_errno == 0);
+
+ /* verify preservation across switch */
+ errno = EXDEV;
+
+ wait_for_state(T1_START);
+
+ t1_tid = pthread_self();
+ ASSERT(pthread_equal(main_tid, t1_tid) == 0);
+ ASSERT(&errno == t1_errno);
+
+ ASSERTe(*t1_errno, == EXDEV);
+
+ set_state(T1_SIGNAL);
+ ASSERT(&errno == t1_errno);
+ wait_for_state(T1_CHECK2);
+ ASSERTe(*t1_errno, == ENODEV);
+
+ ASSERT(&errno == t1_errno);
+ ASSERT(pthread_equal(t1_tid, pthread_self()));
+
+ set_state(T1_EXIT);
+ return (NULL);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sigaction act;
+ int r;
+
+ pthread_mutex_init(&m, NULL);
+ pthread_cond_init(&c, NULL);
+ state = START;
+
+ main_errno = &errno;
+ main_tid = pthread_self();
+
+ act.sa_handler = act_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ errno = 0;
+ CHECKe(sigaction(SIGUSR1, &act, NULL));
+ ASSERT(*main_errno == 0);
+ ASSERT(errno == 0);
+
+ /*
+ * we'll use SIGUSR2 for signal state change from act_handler,
+ * detecting it with sigwait(), so block it now
+ */
+ CHECKe(sigaction(SIGUSR2, &act, NULL));
+ sigemptyset(&sigusr2);
+ sigaddset(&sigusr2, SIGUSR2);
+ CHECKr(pthread_sigmask(SIG_BLOCK, &sigusr2, NULL));
+
+ sched_yield();
+ ASSERT(&errno == main_errno);
+
+ /* do something to force an error */
+ r = close(11);
+ if (r != 0) {
+ ASSERT(r == -1);
+ ASSERTe(*main_errno, == EBADF);
+ ASSERTe(errno, == EBADF);
+ }
+ r = write(11, "", 1);
+ ASSERT(r == -1);
+ ASSERTe(*main_errno, == EBADF);
+ ASSERTe(errno, == EBADF);
+
+ /* verify that a succesfull syscall doesn't change errno */
+ CHECKe(write(STDOUT_FILENO, "X", 1));
+ ASSERTe(*main_errno, == EBADF);
+ ASSERTe(errno, == EBADF);
+ ASSERT(&errno == main_errno);
+
+ CHECKr(pthread_create(&t1_tid, NULL, tmain, NULL));
+ ASSERTe(*main_errno, == EBADF);
+ ASSERT(&errno == main_errno);
+ ASSERT(pthread_equal(main_tid, pthread_self()));
+
+ set_state(T1_START);
+ ASSERTe(*main_errno, == EBADF);
+ ASSERT(&errno == main_errno);
+
+ wait_for_state(T1_SIGNAL);
+ ASSERTe(*main_errno, == EBADF);
+ ASSERT(&errno == main_errno);
+ ASSERT(pthread_equal(main_tid, pthread_self()));
+
+
+ handler_errno = &t1_errno;
+ CHECKe(pthread_kill(t1_tid, SIGUSR1));
+ ASSERT(&errno == main_errno);
+
+ CHECKr(sigwait(&sigusr2, &r));
+ ASSERTe(*main_errno, == EBADF);
+ ASSERT(&errno == main_errno);
+ ASSERT(pthread_equal(main_tid, pthread_self()));
+ set_state(T1_CHECK2);
+
+ wait_for_state(T1_EXIT);
+ CHECKe(pthread_join(t1_tid, NULL));
+ ASSERT(&errno == main_errno);
+ ASSERT(pthread_equal(main_tid, pthread_self()));
+
+ SUCCEED;
+}