static void _execute_timer_events( int *next_timeout_usec ) { assert( next_timeout_usec != NULL ); timer_read_begin(); struct timer_info *timer = get_timer_info(); timer_read_end(); assert( timer != NULL ); debug( "Executing timer events ( timer_callbacks = %p ).", timer->timer_callbacks ); struct timespec now = { 0, 0 }; assert( clock_gettime( CLOCK_MONOTONIC, &now ) == 0 ); assert( timer->timer_callbacks != NULL ); timer_callback_info *callback = NULL; dlist_element *element_next = NULL; for ( dlist_element *element = timer->timer_callbacks->next; element; element = element_next ) { element_next = element->next; callback = element->data; if ( callback->function != NULL ) { if ( TIMESPEC_LESS_THEN( &now, &callback->expires_at ) ) { break; } on_timer( callback, &now ); } delete_dlist_element( element ); if ( callback->function == NULL ) { xfree( callback ); } else { insert_timer_callback( timer, callback ); } } struct timespec max_timeout = { ( INT_MAX / 1000000 ), 0 }; struct timespec min_timeout = { 0, 0 }; if ( timer->timer_callbacks->next == NULL ) { TIMESPEC_TO_MICROSECONDS( &max_timeout, next_timeout_usec ); } else { callback = timer->timer_callbacks->next->data; if ( TIMESPEC_LESS_THEN( &callback->expires_at, &now ) ) { TIMESPEC_TO_MICROSECONDS( &min_timeout, next_timeout_usec ); } else { struct timespec timeout = { 0, 0 }; SUB_TIMESPEC( &callback->expires_at, &now, &timeout ); if ( TIMESPEC_LESS_THEN( &timeout, &max_timeout ) ) { TIMESPEC_TO_MICROSECONDS( &timeout, next_timeout_usec ); } else { TIMESPEC_TO_MICROSECONDS( &max_timeout, next_timeout_usec ); } } } }
static bool _add_timer_event_callback( struct itimerspec *interval, timer_callback callback, void *user_data ) { assert( interval != NULL ); assert( callback != NULL ); timer_read_begin(); struct timer_info *timer = get_timer_info(); timer_read_end(); assert( timer != NULL ); debug( "Adding a timer event callback ( interval = %u.%09u, initial expiration = %u.%09u, callback = %p, user_data = %p ).", interval->it_interval.tv_sec, interval->it_interval.tv_nsec, interval->it_value.tv_sec, interval->it_value.tv_nsec, callback, user_data ); timer_callback_info *cb; struct timespec now; cb = xmalloc( sizeof( timer_callback_info ) ); memset( cb, 0, sizeof( timer_callback_info ) ); cb->function = callback; cb->user_data = user_data; if ( clock_gettime( CLOCK_MONOTONIC, &now ) != 0 ) { error( "Failed to retrieve monotonic time ( %s [%d] ).", strerror( errno ), errno ); xfree( cb ); return false; } cb->interval = interval->it_interval; if ( VALID_TIMESPEC( &interval->it_value ) ) { ADD_TIMESPEC( &now, &interval->it_value, &cb->expires_at ); } else if ( VALID_TIMESPEC( &interval->it_interval ) ) { ADD_TIMESPEC( &now, &interval->it_interval, &cb->expires_at ); } else { error( "Timer must not be zero when a timer event is added." ); xfree( cb ); return false; } debug( "Set an initial expiration time to %u.%09u.", now.tv_sec, now.tv_nsec ); assert( timer->timer_callbacks != NULL ); insert_timer_callback( timer, cb ); return true; }