diff --git a/include/services.h b/include/services.h new file mode 100644 index 00000000000..4f8f054dd4f --- /dev/null +++ b/include/services.h @@ -0,0 +1,28 @@ +/* + * Kernel Service Thread API + * + * Copyright 1999 Ulrich Weigand + */ + +#ifndef __WINE_SERVICES_H +#define __WINE_SERVICES_H + +#include "winbase.h" + +BOOL SERVICE_Init( void ); + +HANDLE SERVICE_AddObject( HANDLE object, + PAPCFUNC callback, ULONG_PTR callback_arg ); + +HANDLE SERVICE_AddTimer( LONG rate, + PAPCFUNC callback, ULONG_PTR callback_arg ); + +BOOL SERVICE_Delete( HANDLE service ); + +BOOL SERVICE_Enable( HANDLE service ); + +BOOL SERVICE_Disable( HANDLE service ); + + +#endif /* __WINE_SERVICES_H */ + diff --git a/loader/main.c b/loader/main.c index 2d3447d8e56..707f16db633 100644 --- a/loader/main.c +++ b/loader/main.c @@ -39,6 +39,7 @@ #include "shell.h" #include "winproc.h" #include "syslevel.h" +#include "services.h" #include "thread.h" #include "task.h" #include "debug.h" @@ -62,6 +63,9 @@ BOOL MAIN_MainInit(void) /* Initialize signal handling */ if (!SIGNAL_Init()) return FALSE; + /* Initialize kernel services thread */ + if (!SERVICE_Init()) return FALSE; + /* Load the configuration file */ if (!PROFILE_LoadWineIni()) return FALSE; diff --git a/scheduler/Makefile.in b/scheduler/Makefile.in index 1b1f87b774e..a68fcabf518 100644 --- a/scheduler/Makefile.in +++ b/scheduler/Makefile.in @@ -14,6 +14,7 @@ C_SRCS = \ pipe.c \ process.c \ semaphore.c \ + services.c \ synchro.c \ sysdeps.c \ syslevel.c \ diff --git a/scheduler/services.c b/scheduler/services.c new file mode 100644 index 00000000000..a4f8de5d60b --- /dev/null +++ b/scheduler/services.c @@ -0,0 +1,366 @@ +/* + * Kernel Services Thread + * + * Copyright 1999 Ulrich Weigand + */ + +#include +#include + +#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; +} + +