From ff228ee519c27bb80234e6eadbf9cbe1adda4318 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 27 Aug 2019 23:20:20 +0200 Subject: [SERVER] Add timer infrastructure To be used by "close unused fd" checks, and others... --- src/server/server.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++---- src/server/server.h | 2 +- 2 files changed, 134 insertions(+), 10 deletions(-) diff --git a/src/server/server.c b/src/server/server.c index 922740a..1cdd2ab 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -45,6 +45,25 @@ #define LONGOPT_SIZE 1004 #define LONGOPT_ERRORMSG 1005 +typedef struct _job job_t; + +struct _job { + job_t *next; + void *(*startRoutine)(void *); + void *arg; + ticks dueDate; + int intervalSecs; +}; + +static job_t *jobHead; +static _Atomic(job_t *) newJob; +static bool hasTimerThread = false; +static pthread_t timerThread; + +static pthread_t mainThread; + +#define DEFAULT_TIMER_TIMEOUT (60) + static poll_list_t *listeners = NULL; /** @@ -71,6 +90,12 @@ static void dnbd3_handleSignal2(int signum, siginfo_t *info, void *data); static void* server_asyncImageListLoad(void *data); +static void* timerMainloop(void*); + +static int handlePendingJobs(void); + +static void queueJobInternal(job_t *job); + /** * Print help text for usage instructions */ @@ -105,14 +130,21 @@ void dnbd3_printVersion() /** * Clean up structs, connections, write out data, then exit */ -void dnbd3_cleanup() +_Noreturn static void dnbd3_cleanup() { int retries; _shutdown = true; logadd( LOG_INFO, "Cleanup..." ); - if ( listeners != NULL ) sock_destroyPollList( listeners ); + if ( hasTimerThread ) { + pthread_kill( timerThread, SIGHUP ); + thread_join( timerThread, NULL ); + } + + if ( listeners != NULL ) { + sock_destroyPollList( listeners ); + } listeners = NULL; // Kill connection to all clients @@ -172,6 +204,7 @@ int main(int argc, char *argv[]) { 0, 0, 0, 0 } }; + mainThread = pthread_self(); opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); while ( opt != -1 ) { @@ -195,8 +228,12 @@ int main(int argc, char *argv[]) case LONGOPT_CRC4: return image_generateCrcFile( optarg ) ? 0 : EXIT_FAILURE; case LONGOPT_ASSERT: + printf( "Now leaking memory:\n" ); + char *bla = malloc( 10 ); + bla[2] = 3; + bla = NULL; printf( "Testing use after free:\n" ); - volatile char * volatile test = malloc( 10 ); + char *test = malloc( 10 ); test[0] = 1; free( (void*)test ); test[1] = 2; @@ -303,11 +340,10 @@ int main(int argc, char *argv[]) } // setup signal handler - struct sigaction sa; - memset( &sa, 0, sizeof(sa) ); - sa.sa_sigaction = dnbd3_handleSignal2; - sa.sa_flags = SA_SIGINFO; - //sa.sa_mask = ; + struct sigaction sa = { + .sa_sigaction = dnbd3_handleSignal2, + .sa_flags = SA_SIGINFO, + }; sigaction( SIGTERM, &sa, NULL ); sigaction( SIGINT, &sa, NULL ); sigaction( SIGUSR1, &sa, NULL ); @@ -342,6 +378,10 @@ int main(int argc, char *argv[]) logadd( LOG_INFO, "Server is ready. (%s)", VERSION_STRING ); + if ( thread_create( &timerThread, NULL, &timerMainloop, NULL ) == 0 ) { + hasTimerThread = true; + } + // +++++++++++++++++++++++++++++++++++++++++++++++++++ main loop struct sockaddr_storage client; socklen_t len; @@ -365,7 +405,7 @@ int main(int argc, char *argv[]) // len = sizeof(client); fd = sock_accept( listeners, &client, &len ); - if ( fd < 0 ) { + if ( fd == -1 ) { const int err = errno; if ( err == EINTR || err == EAGAIN ) continue; logadd( LOG_ERROR, "Client accept failure (err=%d)", err ); @@ -469,6 +509,8 @@ static void dnbd3_handleSignal(int signum) static void dnbd3_handleSignal2(int signum, siginfo_t *info, void *data UNUSED) { + if ( !pthread_equal( pthread_self(), mainThread ) ) + return; memcpy( &lastSignal, info, sizeof(siginfo_t) ); dnbd3_handleSignal( signum ); } @@ -488,3 +530,85 @@ static void* server_asyncImageListLoad(void *data UNUSED) return NULL; } +static void* timerMainloop(void* stuff UNUSED) +{ + setThreadName( "timer" ); + while ( !_shutdown ) { + // Handle jobs/timer events; returns timeout until next event + int to = handlePendingJobs(); + sleep( MIN( MAX( 1, to ), DEFAULT_TIMER_TIMEOUT ) ); + } + logadd( LOG_DEBUG1, "Timer thread done" ); + return NULL; +} + +static int handlePendingJobs(void) +{ + declare_now; + job_t *todo, **temp, *old; + int diff; + todo = jobHead; + for ( temp = &todo; *temp != NULL; temp = &(*temp)->next ) { + diff = (int)timing_diff( &now, &(*temp)->dueDate ); + if ( diff > 0 ) // Found one that's in the future + break; + } + jobHead = *temp; // Make it list head + *temp = NULL; // Split off part before that + while ( todo != NULL ) { + threadpool_run( todo->startRoutine, todo->arg ); + old = todo; + todo = todo->next; + if ( old->intervalSecs == 0 ) { + free( old ); // oneshot + } else { + timing_set( &old->dueDate, &now, old->intervalSecs ); + queueJobInternal( old ); // repeated + } + } + // See if any new jobs have been queued + while ( newJob != NULL ) { + todo = newJob; + // NULL should never happen since we're the only consumer + assert( todo != NULL ); + if ( !atomic_compare_exchange_weak( &newJob, &todo, NULL ) ) + continue; + do { + old = todo; + todo = todo->next; + queueJobInternal( old ); + } while ( todo != NULL ); + } + // Return new timeout + if ( jobHead == NULL ) + return DEFAULT_TIMER_TIMEOUT; + return (int)timing_diff( &now, &jobHead->dueDate ); +} + +static void queueJobInternal(job_t *job) +{ + assert( job != NULL ); + job_t **it; + for ( it = &jobHead; *it != NULL; it = &(*it)->next ) { + if ( timing_1le2( &job->dueDate, &(*it)->dueDate ) ) + break; + } + job->next = *it; + *it = job; +} + +void server_addJob(void *(*startRoutine)(void *), void *arg, int delaySecs, int intervalSecs) +{ + declare_now; + job_t *new = malloc( sizeof(*new) ); + new->startRoutine = startRoutine; + new->arg = arg; + new->intervalSecs = intervalSecs; + timing_set( &new->dueDate, &now, delaySecs ); + for ( ;; ) { + new->next = newJob; + if ( atomic_compare_exchange_weak( &newJob, &new->next, new ) ) + break; + } +} + diff --git a/src/server/server.h b/src/server/server.h index bab8421..a026eb6 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -24,8 +24,8 @@ #include "globals.h" #include "../types.h" -void dnbd3_cleanup(); uint32_t dnbd3_serverUptime(); +void server_addJob(void *(*startRoutine)(void *), void *arg, int delaySecs, int intervalSecs); #if !defined(_FILE_OFFSET_BITS) || _FILE_OFFSET_BITS != 64 #error Please set _FILE_OFFSET_BITS to 64 in your makefile/configuration -- cgit v1.2.3-55-g7522