diff options
author | Philip Guenthe <guenther@cvs.openbsd.org> | 2011-11-18 05:30:46 +0000 |
---|---|---|
committer | Philip Guenthe <guenther@cvs.openbsd.org> | 2011-11-18 05:30:46 +0000 |
commit | e1e017930a787c7ded2b25344d51c70924de2814 (patch) | |
tree | 1446f52f2d49c88e3543417cd303a7a6e7e2f81c | |
parent | 02f627af3b990547e5f7d6fd233a2f9069a8e32f (diff) |
Regress test for per-(r)thread errno address
-rw-r--r-- | regress/lib/libpthread/errno/Makefile | 5 | ||||
-rw-r--r-- | regress/lib/libpthread/errno/errno.c | 196 |
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; +} |