2024-01-22 14:02:59 +01:00
# include "timer.h"
# include "../common.h"
# include "../helper.h"
2024-01-23 05:15:21 +01:00
dispatcher : : timer : : timer ( ) {
this - > active_callbacks = 0 ;
for ( auto & entry : handles ) {
entry = INVALID_HANDLE_VALUE ;
}
}
2024-01-22 14:02:59 +01:00
dispatcher : : timer : : ~ timer ( ) { }
HANDLE dispatcher : : timer : : create_timer_object ( ) {
return CreateWaitableTimer ( nullptr , false , nullptr ) ;
}
bool dispatcher : : timer : : set_timer_object ( HANDLE handle , LARGE_INTEGER * due_time ,
unsigned long period ) {
return SetWaitableTimer ( handle , due_time , period , nullptr , nullptr , false ) > 0
? true
: false ;
}
2024-01-23 05:15:21 +01:00
dispatcher : : timer : : callback : : callback ( std : : function < void ( ) > routine ,
int due_time_seconds ,
int period_seconds ) {
this - > callback_routine = routine ;
this - > due_time . QuadPart = helper : : seconds_to_nanoseconds ( due_time_seconds ) ;
this - > period = helper : : seconds_to_milliseconds ( period_seconds ) ;
}
std : : optional < HANDLE >
dispatcher : : timer : : insert_callback ( std : : function < void ( ) > routine ,
int due_time_seconds , int period_seconds ) {
2024-01-22 14:02:59 +01:00
std : : lock_guard < std : : mutex > lock ( this - > lock ) ;
2024-01-23 05:15:21 +01:00
std : : optional < HANDLE * > handle = this - > find_free_handle ( ) ;
if ( ! handle . has_value ( ) ) {
2024-01-22 14:02:59 +01:00
LOG_ERROR ( " No free event handles available. Unable to create timer. " ) ;
2024-01-23 05:15:21 +01:00
return { } ;
2024-01-22 14:02:59 +01:00
}
2024-01-23 05:15:21 +01:00
* handle . value ( ) = create_timer_object ( ) ;
if ( * handle . value ( ) = = NULL ) {
2024-01-22 14:02:59 +01:00
LOG_ERROR ( " CreateWaitableTimer failed with status %x " , GetLastError ( ) ) ;
2024-01-23 05:15:21 +01:00
return { } ;
2024-01-22 14:02:59 +01:00
}
2024-01-23 05:15:21 +01:00
callback cb ( routine , due_time_seconds , period_seconds ) ;
if ( ! set_timer_object ( * handle . value ( ) , & cb . due_time , cb . period ) ) {
2024-01-22 14:02:59 +01:00
LOG_ERROR ( " SetWaitableTimer failed with status %x " , GetLastError ( ) ) ;
}
2024-01-23 05:15:21 +01:00
std : : pair < HANDLE , callback > entry ( * handle . value ( ) , cb ) ;
this - > callbacks . insert ( entry ) ;
this - > insert_handle ( * handle . value ( ) ) ;
2024-01-22 14:02:59 +01:00
this - > active_callbacks + + ;
2024-01-23 05:15:21 +01:00
return * handle . value ( ) ;
2024-01-22 14:02:59 +01:00
}
/* assumes lock is held by caller */
2024-01-23 05:15:21 +01:00
std : : optional < HANDLE * > dispatcher : : timer : : find_free_handle ( ) {
2024-01-22 14:02:59 +01:00
for ( int index = 0 ; index < MAXIMUM_WAIT_OBJECTS ; index + + ) {
2024-01-23 05:15:21 +01:00
if ( handles [ index ] = = INVALID_HANDLE_VALUE )
return & handles [ index ] ;
2024-01-22 14:02:59 +01:00
}
return { } ;
}
2024-01-23 05:15:21 +01:00
/* assumes lock is held */
void dispatcher : : timer : : insert_handle ( HANDLE handle ) {
for ( HANDLE entry : this - > handles ) {
if ( entry = = INVALID_HANDLE_VALUE ) {
entry = handle ;
return ;
2024-01-22 14:02:59 +01:00
}
}
}
2024-01-23 05:15:21 +01:00
/* assumes lock is held */
void dispatcher : : timer : : close_handle_entry ( HANDLE handle ) {
this - > callbacks . erase ( handle ) ;
for ( int entry = 0 ; entry < MAXIMUM_WAIT_OBJECTS ; entry + + ) {
if ( this - > handles [ entry ] = = handle ) {
CloseHandle ( handle ) ;
this - > handles [ entry ] = INVALID_HANDLE_VALUE ;
2024-01-25 12:09:16 +01:00
/* ordering doesnt matter, as long as the valid handles are at the front
* of the array and are contiguous */
2024-01-23 05:15:21 +01:00
std : : sort ( this - > handles . begin ( ) , this - > handles . end ( ) ) ;
this - > active_callbacks - - ;
return ;
}
}
2024-01-22 14:02:59 +01:00
}
void dispatcher : : timer : : dispatch_callback_for_index ( unsigned long index ) {
2024-01-23 05:15:21 +01:00
std : : unordered_map < HANDLE , callback > : : const_iterator it =
this - > callbacks . find ( handles [ index ] ) ;
if ( it = = this - > callbacks . end ( ) )
return ;
it - > second . callback_routine ( ) ;
}
/* assumes lock is held */
void dispatcher : : timer : : query_removal_queue ( ) {
if ( callbacks_to_remove . empty ( ) )
return ;
while ( ! callbacks_to_remove . empty ( ) ) {
HANDLE entry = callbacks_to_remove . front ( ) ;
this - > close_handle_entry ( entry ) ;
this - > callbacks_to_remove . pop ( ) ;
2024-01-25 12:09:16 +01:00
this - > active_callbacks - - ;
2024-01-23 05:15:21 +01:00
}
2024-01-22 14:02:59 +01:00
}
2024-01-25 12:09:16 +01:00
/* todo: maybe have an event object that we can wait on whilst no events are queued, then when we do insert an event alert the event object ? though this isnt urgent for our use case...*/
2024-01-22 14:02:59 +01:00
void dispatcher : : timer : : run_timer_thread ( ) {
2024-01-25 12:09:16 +01:00
if ( this - > active_callbacks = = 0 )
return ;
2024-01-22 14:02:59 +01:00
while ( true ) {
unsigned long index = WaitForMultipleObjects (
this - > active_callbacks , reinterpret_cast < HANDLE * > ( & handles ) , false ,
INFINITE ) ;
2024-01-23 05:15:21 +01:00
{
std : : lock_guard < std : : mutex > lock ( this - > lock ) ;
this - > dispatch_callback_for_index ( index ) ;
this - > query_removal_queue ( ) ;
2024-01-25 12:09:16 +01:00
/* maybe we should have some default event ? */
if ( this - > active_callbacks = = 0 )
return ;
2024-01-23 05:15:21 +01:00
}
2024-01-22 14:02:59 +01:00
}
2024-01-23 05:15:21 +01:00
}
/*
* If we remove a callback whilst the main loop is sleeping , it means the
* information passed to KeWaitForMultipleObjects will be wrong , hence we need
* to wait until our thread is run by the scheduler , perform the operation for
* the alerted handle and THEN remove any entries from the removal queue . Then
* once we recall KeWaitForMultipleObjects the new handle array will be valid .
*/
void dispatcher : : timer : : remove_callback ( HANDLE handle ) {
std : : lock_guard < std : : mutex > lock ( this - > lock ) ;
this - > callbacks_to_remove . push ( handle ) ;
2024-01-22 14:02:59 +01:00
}