blob: 68fc4d6b9418f13c6429e35a8a1b0fba0e59ae52 [file] [log] [blame]
/*
* Kernel Services Thread
*
* Copyright 1999 Ulrich Weigand
*/
#include <sys/time.h>
#include <unistd.h>
#include "services.h"
#include "debugtools.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("Waiting for %d objects with timeout %ld\n",
count, timeout );
retval = WaitForMultipleObjectsEx( count, handles,
FALSE, timeout, TRUE );
TRACE("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;
}