| /* |
| * Kernel Services Thread |
| * |
| * Copyright 1999 Ulrich Weigand |
| */ |
| |
| #include <sys/time.h> |
| #include <unistd.h> |
| |
| #include "services.h" |
| #include "debugtools.h" |
| |
| DEFAULT_DEBUG_CHANNEL(timer); |
| |
| typedef struct _SERVICE |
| { |
| struct _SERVICE *next; |
| HANDLE self; |
| |
| PAPCFUNC callback; |
| ULONG_PTR callback_arg; |
| |
| BOOL disabled; |
| HANDLE object; |
| } SERVICE; |
| |
| |
| static HANDLE service_thread; |
| static SERVICE *service_first; |
| static DWORD service_counter; |
| |
| /*********************************************************************** |
| * SERVICE_Loop |
| */ |
| static DWORD CALLBACK SERVICE_Loop( void *dummy ) |
| { |
| HANDLE handles[MAXIMUM_WAIT_OBJECTS]; |
| int count = 0; |
| DWORD retval = WAIT_FAILED; |
| |
| while ( TRUE ) |
| { |
| PAPCFUNC callback; |
| ULONG_PTR callback_arg; |
| SERVICE *s; |
| |
| /* Check whether some condition is fulfilled */ |
| |
| HeapLock( GetProcessHeap() ); |
| |
| callback = NULL; |
| callback_arg = 0L; |
| for ( s = service_first; s; s = s->next ) |
| { |
| if (s->disabled) continue; |
| |
| 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; |
| } |
| } |
| } |
| |
| HeapUnlock( GetProcessHeap() ); |
| |
| /* If found, call callback routine */ |
| |
| if ( callback ) |
| { |
| callback( callback_arg ); |
| continue; |
| } |
| |
| /* If not found, determine wait condition */ |
| |
| HeapLock( GetProcessHeap() ); |
| |
| count = 0; |
| for ( s = service_first; s; s = s->next ) |
| { |
| if (s->disabled) continue; |
| |
| if ( count < MAXIMUM_WAIT_OBJECTS ) |
| handles[count++] = s->object; |
| } |
| |
| HeapUnlock( GetProcessHeap() ); |
| |
| |
| /* Wait until some condition satisfied */ |
| |
| TRACE("Waiting for %d objects\n", count ); |
| |
| retval = WaitForMultipleObjectsEx( count, handles, FALSE, INFINITE, TRUE ); |
| |
| TRACE("Wait returned: %ld\n", retval ); |
| } |
| |
| return 0L; |
| } |
| |
| /*********************************************************************** |
| * SERVICE_CreateServiceTable |
| */ |
| static BOOL SERVICE_CreateServiceTable( void ) |
| { |
| /* service_thread must be set *BEFORE* calling CreateThread |
| * otherwise the thread cleanup service will cause an infinite recursion |
| * when installed |
| */ |
| service_thread = INVALID_HANDLE_VALUE; |
| |
| service_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)SERVICE_Loop, |
| NULL, 0, NULL ); |
| if ( service_thread == INVALID_HANDLE_VALUE ) |
| { |
| service_thread = 0; |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * SERVICE_AddObject |
| * |
| * Warning: the object supplied by the caller must not be closed. It'll |
| * be destroyed when the service is deleted. It's up to the caller |
| * to ensure that object will not be destroyed in between. |
| */ |
| HANDLE SERVICE_AddObject( HANDLE object, |
| PAPCFUNC callback, ULONG_PTR callback_arg ) |
| { |
| SERVICE *s; |
| HANDLE handle; |
| |
| if ( object == INVALID_HANDLE_VALUE || !callback ) |
| return INVALID_HANDLE_VALUE; |
| |
| if (!service_thread && !SERVICE_CreateServiceTable()) return INVALID_HANDLE_VALUE; |
| |
| s = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) ); |
| if ( !s ) return INVALID_HANDLE_VALUE; |
| |
| s->callback = callback; |
| s->callback_arg = callback_arg; |
| s->object = object; |
| s->disabled = FALSE; |
| |
| HeapLock( GetProcessHeap() ); |
| |
| s->self = handle = (HANDLE)++service_counter; |
| s->next = service_first; |
| service_first = s; |
| |
| HeapUnlock( GetProcessHeap() ); |
| |
| QueueUserAPC( NULL, service_thread, 0L ); |
| |
| return handle; |
| } |
| |
| /*********************************************************************** |
| * SERVICE_AddTimer |
| */ |
| HANDLE SERVICE_AddTimer( LONG rate, |
| PAPCFUNC callback, ULONG_PTR callback_arg ) |
| { |
| HANDLE handle, ret; |
| LARGE_INTEGER when; |
| |
| if ( !rate || !callback ) |
| return INVALID_HANDLE_VALUE; |
| |
| handle = CreateWaitableTimerA( NULL, FALSE, NULL ); |
| if (!handle) return INVALID_HANDLE_VALUE; |
| |
| if (!rate) rate = 1; |
| when.s.LowPart = when.s.HighPart = 0; |
| if (!SetWaitableTimer( handle, &when, rate, NULL, NULL, FALSE )) |
| { |
| CloseHandle( handle ); |
| return INVALID_HANDLE_VALUE; |
| } |
| |
| if ((ret = SERVICE_AddObject( handle, callback, callback_arg )) == INVALID_HANDLE_VALUE) |
| { |
| CloseHandle( handle ); |
| return INVALID_HANDLE_VALUE; |
| } |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * SERVICE_Delete |
| */ |
| BOOL SERVICE_Delete( HANDLE service ) |
| { |
| HANDLE handle = INVALID_HANDLE_VALUE; |
| BOOL retv = FALSE; |
| SERVICE **s, *next; |
| |
| HeapLock( GetProcessHeap() ); |
| |
| for ( s = &service_first; *s; s = &(*s)->next ) |
| { |
| if ( (*s)->self == service ) |
| { |
| handle = (*s)->object; |
| next = (*s)->next; |
| HeapFree( GetProcessHeap(), 0, *s ); |
| *s = next; |
| retv = TRUE; |
| break; |
| } |
| } |
| |
| HeapUnlock( GetProcessHeap() ); |
| |
| if ( handle != INVALID_HANDLE_VALUE ) |
| CloseHandle( handle ); |
| |
| QueueUserAPC( NULL, service_thread, 0L ); |
| |
| return retv; |
| } |
| |
| /*********************************************************************** |
| * SERVICE_Enable |
| */ |
| BOOL SERVICE_Enable( HANDLE service ) |
| { |
| BOOL retv = FALSE; |
| SERVICE *s; |
| |
| HeapLock( GetProcessHeap() ); |
| |
| for ( s = service_first; s; s = s->next ) |
| { |
| if ( s->self == service ) |
| { |
| s->disabled = FALSE; |
| retv = TRUE; |
| break; |
| } |
| } |
| |
| HeapUnlock( GetProcessHeap() ); |
| |
| QueueUserAPC( NULL, service_thread, 0L ); |
| |
| return retv; |
| } |
| |
| /*********************************************************************** |
| * SERVICE_Disable |
| */ |
| BOOL SERVICE_Disable( HANDLE service ) |
| { |
| BOOL retv = TRUE; |
| SERVICE *s; |
| |
| HeapLock( GetProcessHeap() ); |
| |
| for ( s = service_first; s; s = s->next ) |
| { |
| if ( s->self == service ) |
| { |
| s->disabled = TRUE; |
| retv = TRUE; |
| break; |
| } |
| } |
| |
| HeapUnlock( GetProcessHeap() ); |
| |
| QueueUserAPC( NULL, service_thread, 0L ); |
| |
| return retv; |
| } |
| |
| |