diff --git a/test/testing_darwinunix.c b/test/testing_darwinunix.c index 239421ea..7fa42b4f 100644 --- a/test/testing_darwinunix.c +++ b/test/testing_darwinunix.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "testing.h" #include "testingpriv.h" @@ -60,12 +61,54 @@ out: void testingSleep(int64_t nsec) { - struct timespec duration; + struct timespec duration, remaining; + void (*prevsig)(int); + sigset_t set, prevSet; + struct itimerval setiDuration, prevSetiDuration; + unsigned sec; - // TODO check errors, possibly falling back to usleep, setitimer/pause, or even sleep duration.tv_sec = nsec / testingNsecPerSec; duration.tv_nsec = nsec % testingNsecPerSec; - nanosleep(&duration, NULL); + for (;;) { + errno = 0; + if (nanosleep(&duration, &remaining) == 0) + return; + if (errno != EINTR) + break; + duration = remaining; + } + + // if we got here, nanosleep() failed outright + if (sigemptyset(&set) != 0) + goto fallback; + if (sigaddset(&set, SIGABRT) != 0) + goto fallback; + if (pthread_sigmask(SIG_BLOCK, &set, &prevSet) != 0) + goto fallback; + prevsig = signal(SIGALRM, SIG_IGN); + setiDuration.it_interval.tv_sec = 0; + setiDuration.it_interval.tv_usec = 0; + // keep using duration for this in case nanosleep() was interrupted before it failed + setiDuration.it_value.tv_sec = duration.tv_sec; + setiDuration.it_value.tv_usec = duration.tv_nsec / testingNsecPerUsec; + if (setitimer(ITIMER_REAL, &setiDuration, &prevSetiDuration) != 0) { + pthread_sigmask(SIG_SETMASK, &prevSet, NULL); + signal(SIGALRM, prevsig); + goto fallback; + } + // TODO can this return an errno other than EINTR? + sigsuspend(&prevSet); + setitimer(ITIMER_REAL, &prevSetiDuration, NULL); + signal(SIGALRM, prevsig); + return; + +fallback: + // hopefully we never reach this point, because it has the least granularity of all, but there are no errors, so... + sec = duration.tv_sec; + if (duration.tv_nsec > 0) + sec++; + while (sec > 0) + sec = sleep(sec); } struct testingThread { @@ -85,21 +128,25 @@ static void *threadThreadProc(void *data) testingThread *testingNewThread(void (*f)(void *data), void *data) { testingThread *t; + int err; t = testingprivNew(testingThread); t->f = f; t->data = data; - // TODO check error - pthread_create(&(t->thread), NULL, threadThreadProc, t); + err = pthread_create(&(t->thread), NULL, threadThreadProc, t); + if (err != 0) + testingprivInternalError("error creating thread: %s (%d)", strerror(err), err); return t; } void testingThreadWaitAndFree(testingThread *t) { - // TODO check errors - pthread_join(t->thread, NULL); - // TODO end check errors + int err; + + err = pthread_join(t->thread, NULL); + if (err != 0) + testingprivInternalError("error waiting for thread to finish: %s (%d)", strerror(err), err); // TODO do we need to free t->thread somehow? testingprivFree(t); }