2019-04-28 12:12:40 -05:00
// 28 april 2019
# include <errno.h>
# include <inttypes.h>
# include <setjmp.h>
# include <signal.h>
2019-04-28 20:22:11 -05:00
# include <stdlib.h>
2019-04-28 12:12:40 -05:00
# include <string.h>
2019-04-28 20:22:11 -05:00
# include <time.h>
2019-04-28 12:12:40 -05:00
# include <sys/time.h>
2019-04-28 20:22:11 -05:00
# include <pthread.h>
2019-04-28 12:12:40 -05:00
# include "testing.h"
2019-04-28 18:07:41 -05:00
// TODO don't start the timer on any platform until after we call setjmp(); also decide whether to start the timer before or after resuming the thread on Windows
2019-04-28 13:52:39 -05:00
2019-04-28 12:12:40 -05:00
static jmp_buf timeout_ret ;
static void onTimeout ( int sig )
{
longjmp ( timeout_ret , 1 ) ;
}
2019-04-28 12:26:15 -05:00
void testingprivRunWithTimeout ( testingT * t , const char * file , long line , int64_t timeout , void ( * f ) ( testingT * t , void * data ) , void * data , const char * comment , int failNowOnError )
2019-04-28 12:12:40 -05:00
{
char * timeoutstr ;
2019-04-28 13:52:39 -05:00
void ( * prevsig ) ( int ) ;
2019-04-28 12:12:40 -05:00
struct itimerval timer , prevtimer ;
int setitimerError = 0 ;
2019-04-28 12:19:04 -05:00
timeoutstr = testingNsecString ( timeout ) ;
2019-04-28 12:12:40 -05:00
prevsig = signal ( SIGALRM , onTimeout ) ;
timer . it_interval . tv_sec = 0 ;
timer . it_interval . tv_usec = 0 ;
2019-04-28 12:19:04 -05:00
timer . it_value . tv_sec = timeout / testingNsecPerSec ;
timer . it_value . tv_usec = ( timeout % testingNsecPerSec ) / testingNsecPerUsec ;
2019-04-28 12:12:40 -05:00
if ( setitimer ( ITIMER_REAL , & timer , & prevtimer ) ! = 0 ) {
setitimerError = errno ;
2019-04-28 12:26:15 -05:00
testingprivTLogfFull ( t , file , line , " error applying %s timeout: %s " , comment , strerror ( setitimerError ) ) ;
testingTFail ( t ) ;
2019-04-28 12:12:40 -05:00
goto out ;
}
if ( setjmp ( timeout_ret ) = = 0 ) {
( * f ) ( t , data ) ;
failNowOnError = 0 ; // we succeeded
2019-04-28 12:26:15 -05:00
} else {
testingprivTLogfFull ( t , file , line , " %s timeout passed (%s) " , comment , timeoutstr ) ;
testingTFail ( t ) ;
}
2019-04-28 12:12:40 -05:00
out :
if ( setitimerError = = 0 )
setitimer ( ITIMER_REAL , & prevtimer , NULL ) ;
signal ( SIGALRM , prevsig ) ;
2019-04-28 12:19:04 -05:00
testingFreeNsecString ( timeoutstr ) ;
2019-04-28 12:12:40 -05:00
if ( failNowOnError )
testingTFailNow ( t ) ;
}
2019-04-28 20:22:11 -05:00
void testingSleep ( int64_t nsec )
{
struct timespec rqtp ;
// TODO check errors, possibly falling back to usleep, setitimer/pause, or even sleep
rqtp . tv_sec = nsec / testingNsecPerSec ;
rqtp . tv_nsec = nsec % testingNsecPerSec ;
nanosleep ( & rqtp , NULL ) ;
}
struct testingThread {
pthread_t thread ;
void ( * f ) ( void * data ) ;
void * data ;
} ;
static void * threadThreadProc ( void * data )
{
testingThread * t = ( testingThread * ) data ;
( * ( t - > f ) ) ( t - > data ) ;
return NULL ;
}
testingThread * testingNewThread ( void ( * f ) ( void * data ) , void * data )
{
testingThread * t ;
t = malloc ( sizeof ( testingThread ) ) ;
// TODO check error
memset ( t , 0 , sizeof ( testingThread ) ) ;
t - > f = f ;
t - > data = data ;
// TODO check error
pthread_create ( & ( t - > thread ) , NULL , threadThreadProc , t ) ;
return t ;
}
void testingThreadWaitAndFree ( testingThread * t )
{
// TODO check errors
pthread_join ( t - > thread , NULL ) ;
// TODO end check errors
// TODO do we need to free t->thread somehow?
free ( t ) ;
}