2 * Kernel Services Thread
4 * Copyright 1999 Ulrich Weigand
12 #include "debugtools.h"
14 DEFAULT_DEBUG_CHANNEL(timer)
16 #define SERVICE_USE_OBJECT 0x0001
17 #define SERVICE_USE_TIMEOUT 0x0002
18 #define SERVICE_DISABLED 0x8000
20 typedef struct _SERVICE
22 struct _SERVICE *next;
26 ULONG_PTR callback_arg;
33 struct timeval expire;
37 typedef struct _SERVICETABLE
46 /***********************************************************************
49 static void SERVICE_AddTimeval( struct timeval *time, long delta )
51 delta += time->tv_usec;
52 time->tv_sec += delta / 1000000L;
53 time->tv_usec = delta % 1000000L;
56 /***********************************************************************
59 static long SERVICE_DiffTimeval( struct timeval *time1, struct timeval *time2 )
61 return ( time1->tv_sec - time2->tv_sec ) * 1000000L
62 + ( time1->tv_usec - time2->tv_usec );
65 /***********************************************************************
68 static DWORD CALLBACK SERVICE_Loop( SERVICETABLE *service )
70 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
72 DWORD timeout = INFINITE;
73 DWORD retval = WAIT_FAILED;
78 ULONG_PTR callback_arg;
81 /* Check whether some condition is fulfilled */
83 struct timeval curTime;
84 gettimeofday( &curTime, NULL );
86 HeapLock( GetProcessHeap() );
90 for ( s = service->first; s; s = s->next )
92 if ( s->flags & SERVICE_DISABLED )
95 if ( s->flags & SERVICE_USE_OBJECT )
97 if ( retval >= WAIT_OBJECT_0 && retval < WAIT_OBJECT_0 + count )
99 if ( handles[retval - WAIT_OBJECT_0] == s->object )
101 retval = WAIT_TIMEOUT;
102 callback = s->callback;
103 callback_arg = s->callback_arg;
109 if ( s->flags & SERVICE_USE_TIMEOUT )
111 if ((s->expire.tv_sec < curTime.tv_sec) ||
112 ((s->expire.tv_sec == curTime.tv_sec) &&
113 (s->expire.tv_usec <= curTime.tv_usec)))
115 SERVICE_AddTimeval( &s->expire, s->rate );
116 callback = s->callback;
117 callback_arg = s->callback_arg;
123 HeapUnlock( GetProcessHeap() );
125 /* If found, call callback routine */
129 callback( callback_arg );
133 /* If not found, determine wait condition */
135 HeapLock( GetProcessHeap() );
139 for ( s = service->first; s; s = s->next )
141 if ( s->flags & SERVICE_DISABLED )
144 if ( s->flags & SERVICE_USE_OBJECT )
145 if ( count < MAXIMUM_WAIT_OBJECTS )
146 handles[count++] = s->object;
148 if ( s->flags & SERVICE_USE_TIMEOUT )
150 long delta = SERVICE_DiffTimeval( &s->expire, &curTime );
151 long time = (delta + 999L) / 1000L;
152 if ( time < 1 ) time = 1;
153 if ( time < timeout ) timeout = time;
157 HeapUnlock( GetProcessHeap() );
160 /* Wait until some condition satisfied */
162 TRACE("Waiting for %d objects with timeout %ld\n",
165 retval = WaitForMultipleObjectsEx( count, handles,
166 FALSE, timeout, TRUE );
168 TRACE("Wait returned: %ld\n", retval );
174 /***********************************************************************
175 * SERVICE_CreateServiceTable
177 static BOOL SERVICE_CreateServiceTable( void )
180 SERVICETABLE *service_table;
181 PDB *pdb = PROCESS_Current();
183 service_table = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICETABLE) );
184 if ( !service_table )
189 /* service_table field in PDB must be set *BEFORE* calling CreateThread
190 * otherwise the thread cleanup service will cause an infinite recursion
193 pdb->service_table = service_table;
195 thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)SERVICE_Loop,
196 service_table, 0, NULL );
197 if ( thread == INVALID_HANDLE_VALUE )
199 pdb->service_table = 0;
200 HeapFree( GetProcessHeap(), 0, service_table );
204 service_table->thread = thread;
209 /***********************************************************************
212 * Warning: the object supplied by the caller must not be closed. It'll
213 * be destroyed when the service is deleted. It's up to the caller
214 * to ensure that object will not be destroyed in between.
216 HANDLE SERVICE_AddObject( HANDLE object,
217 PAPCFUNC callback, ULONG_PTR callback_arg )
220 SERVICETABLE *service_table;
223 if ( object == INVALID_HANDLE_VALUE || !callback )
224 return INVALID_HANDLE_VALUE;
226 if (PROCESS_Current()->service_table == 0 && !SERVICE_CreateServiceTable())
227 return INVALID_HANDLE_VALUE;
228 service_table = PROCESS_Current()->service_table;
230 s = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) );
231 if ( !s ) return INVALID_HANDLE_VALUE;
233 s->callback = callback;
234 s->callback_arg = callback_arg;
236 s->flags = SERVICE_USE_OBJECT;
238 HeapLock( GetProcessHeap() );
240 s->self = handle = (HANDLE)++service_table->counter;
241 s->next = service_table->first;
242 service_table->first = s;
244 HeapUnlock( GetProcessHeap() );
246 QueueUserAPC( NULL, service_table->thread, 0L );
251 /***********************************************************************
254 HANDLE SERVICE_AddTimer( LONG rate,
255 PAPCFUNC callback, ULONG_PTR callback_arg )
258 SERVICETABLE *service_table;
261 if ( !rate || !callback )
262 return INVALID_HANDLE_VALUE;
264 if (PROCESS_Current()->service_table == 0 && !SERVICE_CreateServiceTable())
265 return INVALID_HANDLE_VALUE;
266 service_table = PROCESS_Current()->service_table;
268 s = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) );
269 if ( !s ) return INVALID_HANDLE_VALUE;
271 s->callback = callback;
272 s->callback_arg = callback_arg;
274 s->flags = SERVICE_USE_TIMEOUT;
276 gettimeofday( &s->expire, NULL );
277 SERVICE_AddTimeval( &s->expire, s->rate );
279 HeapLock( GetProcessHeap() );
281 s->self = handle = (HANDLE)++service_table->counter;
282 s->next = service_table->first;
283 service_table->first = s;
285 HeapUnlock( GetProcessHeap() );
287 QueueUserAPC( NULL, service_table->thread, 0L );
292 /***********************************************************************
295 BOOL SERVICE_Delete( HANDLE service )
297 HANDLE handle = INVALID_HANDLE_VALUE;
300 SERVICETABLE *service_table;
302 /* service table must have been created on previous SERVICE_Add??? call */
303 if ((service_table = PROCESS_Current()->service_table) == 0)
306 HeapLock( GetProcessHeap() );
308 for ( s = &service_table->first; *s; s = &(*s)->next )
310 if ( (*s)->self == service )
312 if ( (*s)->flags & SERVICE_USE_OBJECT )
313 handle = (*s)->object;
316 HeapFree( GetProcessHeap(), 0, *s );
323 HeapUnlock( GetProcessHeap() );
325 if ( handle != INVALID_HANDLE_VALUE )
326 CloseHandle( handle );
328 QueueUserAPC( NULL, service_table->thread, 0L );
333 /***********************************************************************
336 BOOL SERVICE_Enable( HANDLE service )
340 SERVICETABLE *service_table;
342 /* service table must have been created on previous SERVICE_Add??? call */
343 if ((service_table = PROCESS_Current()->service_table) == 0)
346 HeapLock( GetProcessHeap() );
348 for ( s = service_table->first; s; s = s->next )
350 if ( s->self == service )
352 if ( s->flags & SERVICE_DISABLED )
354 s->flags &= ~SERVICE_DISABLED;
356 if ( s->flags & SERVICE_USE_TIMEOUT )
359 struct timeval curTime;
360 gettimeofday( &curTime, NULL );
362 delta = SERVICE_DiffTimeval( &s->expire, &curTime );
364 SERVICE_AddTimeval( &s->expire,
365 (delta / s->rate) * s->rate );
373 HeapUnlock( GetProcessHeap() );
375 QueueUserAPC( NULL, service_table->thread, 0L );
380 /***********************************************************************
383 BOOL SERVICE_Disable( HANDLE service )
387 SERVICETABLE *service_table;
389 /* service table must have been created on previous SERVICE_Add??? call */
390 if ((service_table = PROCESS_Current()->service_table) == 0)
393 HeapLock( GetProcessHeap() );
395 for ( s = service_table->first; s; s = s->next )
397 if ( s->self == service )
399 s->flags |= SERVICE_DISABLED;
405 HeapUnlock( GetProcessHeap() );
407 QueueUserAPC( NULL, service_table->thread, 0L );