Resolved some TODOs in testing_darwinunix.c, including writing a more elaborate implementation of testingSleep().
This commit is contained in:
parent
7d29c4346d
commit
796a9cf010
|
@ -8,6 +8,7 @@
|
|||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue