Implemented 'kernel services thread'.
diff --git a/scheduler/services.c b/scheduler/services.c
new file mode 100644
index 0000000..a4f8de5
--- /dev/null
+++ b/scheduler/services.c
@@ -0,0 +1,366 @@
+/*
+ * Kernel Services Thread
+ *
+ * Copyright 1999 Ulrich Weigand
+ */
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "services.h"
+#include "debug.h"
+
+
+#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 )
+ {
+ callback = s->callback;
+ callback_arg = s->callback_arg;
+ break;
+ }
+
+ if ( s->flags & SERVICE_USE_TIMEOUT )
+ if ( timercmp( &s->expire, &curTime, < ) )
+ {
+ 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 )
+ 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_AddObject
+ */
+HANDLE SERVICE_AddObject( HANDLE object,
+ PAPCFUNC callback, ULONG_PTR callback_arg )
+{
+ SERVICE *s;
+ HANDLE handle;
+
+ 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 )
+{
+ 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 = (*s)->next;
+ HeapFree( Service->heap, 0, *s );
+ retv = FALSE;
+ break;
+ }
+
+ HeapUnlock( Service->heap );
+
+ 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;
+}
+
+