mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 12:54:13 +00:00
412 lines
10 KiB
C
412 lines
10 KiB
C
/*
|
|
* Kernel Services Thread
|
|
*
|
|
* Copyright 1999 Ulrich Weigand
|
|
*/
|
|
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "services.h"
|
|
#include "process.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 _SERVICETABLE
|
|
{
|
|
HANDLE thread;
|
|
|
|
SERVICE *first;
|
|
DWORD counter;
|
|
|
|
} SERVICETABLE;
|
|
|
|
/***********************************************************************
|
|
* 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( GetProcessHeap() );
|
|
|
|
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( GetProcessHeap() );
|
|
|
|
/* If found, call callback routine */
|
|
|
|
if ( callback )
|
|
{
|
|
callback( callback_arg );
|
|
continue;
|
|
}
|
|
|
|
/* If not found, determine wait condition */
|
|
|
|
HeapLock( GetProcessHeap() );
|
|
|
|
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( GetProcessHeap() );
|
|
|
|
|
|
/* 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_CreateServiceTable
|
|
*/
|
|
static BOOL SERVICE_CreateServiceTable( void )
|
|
{
|
|
HANDLE thread;
|
|
SERVICETABLE *service_table;
|
|
PDB *pdb = PROCESS_Current();
|
|
|
|
service_table = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICETABLE) );
|
|
if ( !service_table )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* service_table field in PDB must be set *BEFORE* calling CreateThread
|
|
* otherwise the thread cleanup service will cause an infinite recursion
|
|
* when installed
|
|
*/
|
|
pdb->service_table = service_table;
|
|
|
|
thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)SERVICE_Loop,
|
|
service_table, 0, NULL );
|
|
if ( thread == INVALID_HANDLE_VALUE )
|
|
{
|
|
pdb->service_table = 0;
|
|
HeapFree( GetProcessHeap(), 0, service_table );
|
|
return FALSE;
|
|
}
|
|
|
|
service_table->thread = thread;
|
|
|
|
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;
|
|
SERVICETABLE *service_table;
|
|
HANDLE handle;
|
|
|
|
if ( object == INVALID_HANDLE_VALUE || !callback )
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
if (PROCESS_Current()->service_table == 0 && !SERVICE_CreateServiceTable())
|
|
return INVALID_HANDLE_VALUE;
|
|
service_table = PROCESS_Current()->service_table;
|
|
|
|
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->flags = SERVICE_USE_OBJECT;
|
|
|
|
HeapLock( GetProcessHeap() );
|
|
|
|
s->self = handle = (HANDLE)++service_table->counter;
|
|
s->next = service_table->first;
|
|
service_table->first = s;
|
|
|
|
HeapUnlock( GetProcessHeap() );
|
|
|
|
QueueUserAPC( NULL, service_table->thread, 0L );
|
|
|
|
return handle;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SERVICE_AddTimer
|
|
*/
|
|
HANDLE SERVICE_AddTimer( LONG rate,
|
|
PAPCFUNC callback, ULONG_PTR callback_arg )
|
|
{
|
|
SERVICE *s;
|
|
SERVICETABLE *service_table;
|
|
HANDLE handle;
|
|
|
|
if ( !rate || !callback )
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
if (PROCESS_Current()->service_table == 0 && !SERVICE_CreateServiceTable())
|
|
return INVALID_HANDLE_VALUE;
|
|
service_table = PROCESS_Current()->service_table;
|
|
|
|
s = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) );
|
|
if ( !s ) return INVALID_HANDLE_VALUE;
|
|
|
|
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 );
|
|
|
|
HeapLock( GetProcessHeap() );
|
|
|
|
s->self = handle = (HANDLE)++service_table->counter;
|
|
s->next = service_table->first;
|
|
service_table->first = s;
|
|
|
|
HeapUnlock( GetProcessHeap() );
|
|
|
|
QueueUserAPC( NULL, service_table->thread, 0L );
|
|
|
|
return handle;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SERVICE_Delete
|
|
*/
|
|
BOOL SERVICE_Delete( HANDLE service )
|
|
{
|
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
|
BOOL retv = FALSE;
|
|
SERVICE **s, *next;
|
|
SERVICETABLE *service_table;
|
|
|
|
/* service table must have been created on previous SERVICE_Add??? call */
|
|
if ((service_table = PROCESS_Current()->service_table) == 0)
|
|
return retv;
|
|
|
|
HeapLock( GetProcessHeap() );
|
|
|
|
for ( s = &service_table->first; *s; s = &(*s)->next )
|
|
{
|
|
if ( (*s)->self == service )
|
|
{
|
|
if ( (*s)->flags & SERVICE_USE_OBJECT )
|
|
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_table->thread, 0L );
|
|
|
|
return retv;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SERVICE_Enable
|
|
*/
|
|
BOOL SERVICE_Enable( HANDLE service )
|
|
{
|
|
BOOL retv = FALSE;
|
|
SERVICE *s;
|
|
SERVICETABLE *service_table;
|
|
|
|
/* service table must have been created on previous SERVICE_Add??? call */
|
|
if ((service_table = PROCESS_Current()->service_table) == 0)
|
|
return retv;
|
|
|
|
HeapLock( GetProcessHeap() );
|
|
|
|
for ( s = service_table->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 = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
HeapUnlock( GetProcessHeap() );
|
|
|
|
QueueUserAPC( NULL, service_table->thread, 0L );
|
|
|
|
return retv;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* SERVICE_Disable
|
|
*/
|
|
BOOL SERVICE_Disable( HANDLE service )
|
|
{
|
|
BOOL retv = TRUE;
|
|
SERVICE *s;
|
|
SERVICETABLE *service_table;
|
|
|
|
/* service table must have been created on previous SERVICE_Add??? call */
|
|
if ((service_table = PROCESS_Current()->service_table) == 0)
|
|
return retv;
|
|
|
|
HeapLock( GetProcessHeap() );
|
|
|
|
for ( s = service_table->first; s; s = s->next )
|
|
{
|
|
if ( s->self == service )
|
|
{
|
|
s->flags |= SERVICE_DISABLED;
|
|
retv = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
HeapUnlock( GetProcessHeap() );
|
|
|
|
QueueUserAPC( NULL, service_table->thread, 0L );
|
|
|
|
return retv;
|
|
}
|
|
|
|
|