/* $OpenBSD: errno.c,v 1.2 2012/02/20 02:34:33 guenther Exp $ */ /* PUBLIC DOMAIN Sep 2011 */ /* * Verify that &errno is different for each thread and is stable across * context switches and in signal handlers */ #include #include #include #include #include #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, "\n", 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; }