/* * Kernel Services Thread * * Copyright 1999 Ulrich Weigand */ #include #include #include "services.h" #include "debug.h" DEFAULT_DEBUG_CHANNEL(timer) #define SERVICE_USE_OBJECT 0x0001 #define SERVICE_USE_TIMEOUT 0x0002 #define SERVICE_DISABLED 0x8000 typedef struct _SERVICE { struct _SERVICE *next; HANDLE self; PAPCFUNC callback; ULONG_PTR callback_arg; int flags; HANDLE object; long rate; struct timeval expire; } SERVICE; typedef struct { HANDLE heap; HANDLE thread; SERVICE *first; DWORD counter; } SERVICETABLE; static SERVICETABLE *Service = NULL; /*********************************************************************** * SERVICE_AddTimeval */ static void SERVICE_AddTimeval( struct timeval *time, long delta ) { delta += time->tv_usec; time->tv_sec += delta / 1000000L; time->tv_usec = delta % 1000000L; } /*********************************************************************** * SERVICE_DiffTimeval */ static long SERVICE_DiffTimeval( struct timeval *time1, struct timeval *time2 ) { return ( time1->tv_sec - time2->tv_sec ) * 1000000L + ( time1->tv_usec - time2->tv_usec ); } /*********************************************************************** * SERVICE_Loop */ static DWORD CALLBACK SERVICE_Loop( SERVICETABLE *service ) { HANDLE handles[MAXIMUM_WAIT_OBJECTS]; int count = 0; DWORD timeout = INFINITE; DWORD retval = WAIT_FAILED; while ( TRUE ) { PAPCFUNC callback; ULONG_PTR callback_arg; SERVICE *s; /* Check whether some condition is fulfilled */ struct timeval curTime; gettimeofday( &curTime, NULL ); HeapLock( service->heap ); callback = NULL; callback_arg = 0L; for ( s = service->first; s; s = s->next ) { if ( s->flags & SERVICE_DISABLED ) continue; if ( s->flags & SERVICE_USE_OBJECT ) if ( retval >= WAIT_OBJECT_0 && retval < WAIT_OBJECT_0 + count ) if ( handles[retval - WAIT_OBJECT_0] == s->object ) { retval = WAIT_TIMEOUT; callback = s->callback; callback_arg = s->callback_arg; break; } if ( s->flags & SERVICE_USE_TIMEOUT ) if ((s->expire.tv_sec < curTime.tv_sec) || ((s->expire.tv_sec == curTime.tv_sec) && (s->expire.tv_usec <= curTime.tv_usec))) { SERVICE_AddTimeval( &s->expire, s->rate ); callback = s->callback; callback_arg = s->callback_arg; break; } } HeapUnlock( service->heap ); /* If found, call callback routine */ if ( callback ) { callback( callback_arg ); continue; } /* If not found, determine wait condition */ HeapLock( service->heap ); count = 0; timeout = INFINITE; for ( s = service->first; s; s = s->next ) { if ( s->flags & SERVICE_DISABLED ) continue; if ( s->flags & SERVICE_USE_OBJECT ) if ( count < MAXIMUM_WAIT_OBJECTS ) handles[count++] = s->object; if ( s->flags & SERVICE_USE_TIMEOUT ) { long delta = SERVICE_DiffTimeval( &s->expire, &curTime ); long time = (delta + 999L) / 1000L; if ( time < 1 ) time = 1; if ( time < timeout ) timeout = time; } } HeapUnlock( service->heap ); /* Wait until some condition satisfied */ TRACE( timer, "Waiting for %d objects with timeout %ld\n", count, timeout ); retval = WaitForMultipleObjectsEx( count, handles, FALSE, timeout, TRUE ); TRACE( timer, "Wait returned: %ld\n", retval ); } return 0L; } /*********************************************************************** * SERVICE_Init */ BOOL SERVICE_Init( void ) { HANDLE heap, thread; heap = HeapCreate( HEAP_GROWABLE, 0x1000, 0 ); if ( !heap ) return FALSE; Service = HeapAlloc( heap, HEAP_ZERO_MEMORY, sizeof(SERVICETABLE) ); if ( !Service ) { HeapDestroy( heap ); return FALSE; } Service->heap = heap; thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)SERVICE_Loop, Service, 0, NULL ); if ( thread == INVALID_HANDLE_VALUE ) { HeapDestroy( heap ); return FALSE; } Service->thread = ConvertToGlobalHandle( thread ); return TRUE; } /*********************************************************************** * SERVICE_Exit */ void SERVICE_Exit( void ) { TerminateThread( Service->thread, 0 ); } /*********************************************************************** * SERVICE_AddObject */ HANDLE SERVICE_AddObject( HANDLE object, PAPCFUNC callback, ULONG_PTR callback_arg ) { SERVICE *s; HANDLE handle; object = ConvertToGlobalHandle( object ); /* FIXME */ if ( !Service || object == INVALID_HANDLE_VALUE || !callback ) return INVALID_HANDLE_VALUE; s = HeapAlloc( Service->heap, HEAP_ZERO_MEMORY, sizeof(SERVICE) ); if ( !s ) return INVALID_HANDLE_VALUE; HeapLock( Service->heap ); s->callback = callback; s->callback_arg = callback_arg; s->object = object; s->flags = SERVICE_USE_OBJECT; s->self = handle = (HANDLE)++Service->counter; s->next = Service->first; Service->first = s; HeapUnlock( Service->heap ); QueueUserAPC( NULL, Service->thread, 0L ); return handle; } /*********************************************************************** * SERVICE_AddTimer */ HANDLE SERVICE_AddTimer( LONG rate, PAPCFUNC callback, ULONG_PTR callback_arg ) { SERVICE *s; HANDLE handle; if ( !Service || !rate || !callback ) return INVALID_HANDLE_VALUE; s = HeapAlloc( Service->heap, HEAP_ZERO_MEMORY, sizeof(SERVICE) ); if ( !s ) return INVALID_HANDLE_VALUE; HeapLock( Service->heap ); s->callback = callback; s->callback_arg = callback_arg; s->rate = rate; s->flags = SERVICE_USE_TIMEOUT; gettimeofday( &s->expire, NULL ); SERVICE_AddTimeval( &s->expire, s->rate ); s->self = handle = (HANDLE)++Service->counter; s->next = Service->first; Service->first = s; HeapUnlock( Service->heap ); QueueUserAPC( NULL, Service->thread, 0L ); return handle; } /*********************************************************************** * SERVICE_Delete */ BOOL SERVICE_Delete( HANDLE service ) { HANDLE handle = INVALID_HANDLE_VALUE; BOOL retv = TRUE; SERVICE **s, *next; if ( !Service ) return retv; HeapLock( Service->heap ); for ( s = &Service->first; *s; s = &(*s)->next ) if ( (*s)->self == service ) { if ( (*s)->flags & SERVICE_USE_OBJECT ) handle = (*s)->object; next = (*s)->next; HeapFree( Service->heap, 0, *s ); *s = next; retv = FALSE; break; } HeapUnlock( Service->heap ); if ( handle != INVALID_HANDLE_VALUE ) CloseHandle( handle ); QueueUserAPC( NULL, Service->thread, 0L ); return retv; } /*********************************************************************** * SERVICE_Enable */ BOOL SERVICE_Enable( HANDLE service ) { BOOL retv = TRUE; SERVICE *s; if ( !Service ) return retv; HeapLock( Service->heap ); for ( s = Service->first; s; s = s->next ) if ( s->self == service ) { if ( s->flags & SERVICE_DISABLED ) { s->flags &= ~SERVICE_DISABLED; if ( s->flags & SERVICE_USE_TIMEOUT ) { long delta; struct timeval curTime; gettimeofday( &curTime, NULL ); delta = SERVICE_DiffTimeval( &s->expire, &curTime ); if ( delta > 0 ) SERVICE_AddTimeval( &s->expire, (delta / s->rate) * s->rate ); } } retv = FALSE; break; } HeapUnlock( Service->heap ); QueueUserAPC( NULL, Service->thread, 0L ); return retv; } /*********************************************************************** * SERVICE_Disable */ BOOL SERVICE_Disable( HANDLE service ) { BOOL retv = TRUE; SERVICE *s; if ( !Service ) return retv; HeapLock( Service->heap ); for ( s = Service->first; s; s = s->next ) if ( s->self == service ) { s->flags |= SERVICE_DISABLED; retv = FALSE; break; } HeapUnlock( Service->heap ); QueueUserAPC( NULL, Service->thread, 0L ); return retv; }