blob: 43bab2d5db4f89ce9e3fc448f63bb29c4f23c1ef [file] [log] [blame]
Ulrich Weigand7761cbe1999-04-11 15:01:20 +00001/*
2 * Kernel Services Thread
3 *
4 * Copyright 1999 Ulrich Weigand
5 */
6
7#include <sys/time.h>
8#include <unistd.h>
9
10#include "services.h"
Eric Pouech63c7cdf1999-06-12 08:24:23 +000011#include "process.h"
Alexandre Julliard15657091999-05-23 10:25:25 +000012#include "debugtools.h"
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000013
Patrik Stridvallb4b9fae1999-04-19 14:56:29 +000014DEFAULT_DEBUG_CHANNEL(timer)
15
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000016#define SERVICE_USE_OBJECT 0x0001
17#define SERVICE_USE_TIMEOUT 0x0002
18#define SERVICE_DISABLED 0x8000
19
20typedef struct _SERVICE
21{
Eric Pouech63c7cdf1999-06-12 08:24:23 +000022 struct _SERVICE *next;
23 HANDLE self;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000024
Eric Pouech63c7cdf1999-06-12 08:24:23 +000025 PAPCFUNC callback;
26 ULONG_PTR callback_arg;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000027
Eric Pouech63c7cdf1999-06-12 08:24:23 +000028 int flags;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000029
Eric Pouech63c7cdf1999-06-12 08:24:23 +000030 HANDLE object;
31 long rate;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000032
Eric Pouech63c7cdf1999-06-12 08:24:23 +000033 struct timeval expire;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000034
35} SERVICE;
36
Eric Pouech63c7cdf1999-06-12 08:24:23 +000037typedef struct _SERVICETABLE
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000038{
Eric Pouech63c7cdf1999-06-12 08:24:23 +000039 HANDLE thread;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000040
Eric Pouech63c7cdf1999-06-12 08:24:23 +000041 SERVICE *first;
42 DWORD counter;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000043
44} SERVICETABLE;
45
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000046/***********************************************************************
47 * SERVICE_AddTimeval
48 */
49static void SERVICE_AddTimeval( struct timeval *time, long delta )
50{
51 delta += time->tv_usec;
52 time->tv_sec += delta / 1000000L;
53 time->tv_usec = delta % 1000000L;
54}
55
56/***********************************************************************
57 * SERVICE_DiffTimeval
58 */
59static long SERVICE_DiffTimeval( struct timeval *time1, struct timeval *time2 )
60{
61 return ( time1->tv_sec - time2->tv_sec ) * 1000000L
62 + ( time1->tv_usec - time2->tv_usec );
63}
64
65/***********************************************************************
66 * SERVICE_Loop
67 */
68static DWORD CALLBACK SERVICE_Loop( SERVICETABLE *service )
69{
Eric Pouech63c7cdf1999-06-12 08:24:23 +000070 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
71 int count = 0;
72 DWORD timeout = INFINITE;
73 DWORD retval = WAIT_FAILED;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000074
75 while ( TRUE )
76 {
77 PAPCFUNC callback;
78 ULONG_PTR callback_arg;
79 SERVICE *s;
80
81 /* Check whether some condition is fulfilled */
82
83 struct timeval curTime;
84 gettimeofday( &curTime, NULL );
85
Eric Pouech63c7cdf1999-06-12 08:24:23 +000086 HeapLock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000087
88 callback = NULL;
89 callback_arg = 0L;
90 for ( s = service->first; s; s = s->next )
91 {
92 if ( s->flags & SERVICE_DISABLED )
93 continue;
94
Eric Pouech63c7cdf1999-06-12 08:24:23 +000095 if ( s->flags & SERVICE_USE_OBJECT )
96 {
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000097 if ( retval >= WAIT_OBJECT_0 && retval < WAIT_OBJECT_0 + count )
Eric Pouech63c7cdf1999-06-12 08:24:23 +000098 {
Ulrich Weigand7761cbe1999-04-11 15:01:20 +000099 if ( handles[retval - WAIT_OBJECT_0] == s->object )
100 {
Ulrich Weigand70b2e381999-05-04 16:43:38 +0000101 retval = WAIT_TIMEOUT;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000102 callback = s->callback;
103 callback_arg = s->callback_arg;
104 break;
105 }
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000106 }
107 }
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000108
109 if ( s->flags & SERVICE_USE_TIMEOUT )
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000110 {
Ove Kaaven3d931021999-05-22 15:56:21 +0000111 if ((s->expire.tv_sec < curTime.tv_sec) ||
112 ((s->expire.tv_sec == curTime.tv_sec) &&
113 (s->expire.tv_usec <= curTime.tv_usec)))
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000114 {
115 SERVICE_AddTimeval( &s->expire, s->rate );
116 callback = s->callback;
117 callback_arg = s->callback_arg;
118 break;
119 }
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000120 }
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000121 }
122
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000123 HeapUnlock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000124
125 /* If found, call callback routine */
126
127 if ( callback )
128 {
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000129 callback( callback_arg );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000130 continue;
131 }
132
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000133 /* If not found, determine wait condition */
134
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000135 HeapLock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000136
137 count = 0;
138 timeout = INFINITE;
139 for ( s = service->first; s; s = s->next )
140 {
141 if ( s->flags & SERVICE_DISABLED )
142 continue;
143
144 if ( s->flags & SERVICE_USE_OBJECT )
Ulrich Weigand70b2e381999-05-04 16:43:38 +0000145 if ( count < MAXIMUM_WAIT_OBJECTS )
146 handles[count++] = s->object;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000147
148 if ( s->flags & SERVICE_USE_TIMEOUT )
149 {
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;
154 }
155 }
156
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000157 HeapUnlock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000158
159
160 /* Wait until some condition satisfied */
161
Alexandre Julliard15657091999-05-23 10:25:25 +0000162 TRACE("Waiting for %d objects with timeout %ld\n",
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000163 count, timeout );
164
165 retval = WaitForMultipleObjectsEx( count, handles,
166 FALSE, timeout, TRUE );
167
Alexandre Julliard15657091999-05-23 10:25:25 +0000168 TRACE("Wait returned: %ld\n", retval );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000169 }
170
171 return 0L;
172}
173
174/***********************************************************************
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000175 * SERVICE_CreateServiceTable
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000176 */
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000177static BOOL SERVICE_CreateServiceTable( void )
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000178{
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000179 HANDLE thread;
180 SERVICETABLE *service_table;
181 PDB *pdb = PROCESS_Current();
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000182
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000183 service_table = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICETABLE) );
184 if ( !service_table )
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000185 {
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000186 return FALSE;
187 }
188
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000189 /* service_table field in PDB must be set *BEFORE* calling CreateThread
190 * otherwise the thread cleanup service will cause an infinite recursion
191 * when installed
192 */
193 pdb->service_table = service_table;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000194
195 thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)SERVICE_Loop,
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000196 service_table, 0, NULL );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000197 if ( thread == INVALID_HANDLE_VALUE )
198 {
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000199 pdb->service_table = 0;
200 HeapFree( GetProcessHeap(), 0, service_table );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000201 return FALSE;
202 }
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000203
204 service_table->thread = thread;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000205
206 return TRUE;
207}
208
209/***********************************************************************
210 * SERVICE_AddObject
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000211 *
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.
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000215 */
216HANDLE SERVICE_AddObject( HANDLE object,
217 PAPCFUNC callback, ULONG_PTR callback_arg )
218{
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000219 SERVICE *s;
220 SERVICETABLE *service_table;
221 HANDLE handle;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000222
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000223 if ( object == INVALID_HANDLE_VALUE || !callback )
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000224 return INVALID_HANDLE_VALUE;
225
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000226 if (PROCESS_Current()->service_table == 0 && !SERVICE_CreateServiceTable())
227 return INVALID_HANDLE_VALUE;
228 service_table = PROCESS_Current()->service_table;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000229
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000230 s = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) );
231 if ( !s ) return INVALID_HANDLE_VALUE;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000232
233 s->callback = callback;
234 s->callback_arg = callback_arg;
235 s->object = object;
236 s->flags = SERVICE_USE_OBJECT;
237
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000238 HeapLock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000239
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000240 s->self = handle = (HANDLE)++service_table->counter;
241 s->next = service_table->first;
242 service_table->first = s;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000243
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000244 HeapUnlock( GetProcessHeap() );
245
246 QueueUserAPC( NULL, service_table->thread, 0L );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000247
248 return handle;
249}
250
251/***********************************************************************
252 * SERVICE_AddTimer
253 */
254HANDLE SERVICE_AddTimer( LONG rate,
255 PAPCFUNC callback, ULONG_PTR callback_arg )
256{
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000257 SERVICE *s;
258 SERVICETABLE *service_table;
259 HANDLE handle;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000260
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000261 if ( !rate || !callback )
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000262 return INVALID_HANDLE_VALUE;
263
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000264 if (PROCESS_Current()->service_table == 0 && !SERVICE_CreateServiceTable())
265 return INVALID_HANDLE_VALUE;
266 service_table = PROCESS_Current()->service_table;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000267
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000268 s = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) );
269 if ( !s ) return INVALID_HANDLE_VALUE;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000270
271 s->callback = callback;
272 s->callback_arg = callback_arg;
273 s->rate = rate;
274 s->flags = SERVICE_USE_TIMEOUT;
275
276 gettimeofday( &s->expire, NULL );
277 SERVICE_AddTimeval( &s->expire, s->rate );
278
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000279 HeapLock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000280
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000281 s->self = handle = (HANDLE)++service_table->counter;
282 s->next = service_table->first;
283 service_table->first = s;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000284
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000285 HeapUnlock( GetProcessHeap() );
286
287 QueueUserAPC( NULL, service_table->thread, 0L );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000288
289 return handle;
290}
291
292/***********************************************************************
293 * SERVICE_Delete
294 */
295BOOL SERVICE_Delete( HANDLE service )
296{
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000297 HANDLE handle = INVALID_HANDLE_VALUE;
298 BOOL retv = FALSE;
299 SERVICE **s, *next;
300 SERVICETABLE *service_table;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000301
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000302 /* service table must have been created on previous SERVICE_Add??? call */
303 if ((service_table = PROCESS_Current()->service_table) == 0)
304 return retv;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000305
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000306 HeapLock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000307
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000308 for ( s = &service_table->first; *s; s = &(*s)->next )
309 {
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000310 if ( (*s)->self == service )
311 {
Ulrich Weigand70b2e381999-05-04 16:43:38 +0000312 if ( (*s)->flags & SERVICE_USE_OBJECT )
313 handle = (*s)->object;
314
315 next = (*s)->next;
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000316 HeapFree( GetProcessHeap(), 0, *s );
Ulrich Weigand70b2e381999-05-04 16:43:38 +0000317 *s = next;
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000318 retv = TRUE;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000319 break;
320 }
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000321 }
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000322
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000323 HeapUnlock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000324
Ulrich Weigand70b2e381999-05-04 16:43:38 +0000325 if ( handle != INVALID_HANDLE_VALUE )
326 CloseHandle( handle );
327
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000328 QueueUserAPC( NULL, service_table->thread, 0L );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000329
330 return retv;
331}
332
333/***********************************************************************
334 * SERVICE_Enable
335 */
336BOOL SERVICE_Enable( HANDLE service )
337{
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000338 BOOL retv = FALSE;
339 SERVICE *s;
340 SERVICETABLE *service_table;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000341
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000342 /* service table must have been created on previous SERVICE_Add??? call */
343 if ((service_table = PROCESS_Current()->service_table) == 0)
344 return retv;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000345
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000346 HeapLock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000347
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000348 for ( s = service_table->first; s; s = s->next )
349 {
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000350 if ( s->self == service )
351 {
352 if ( s->flags & SERVICE_DISABLED )
353 {
354 s->flags &= ~SERVICE_DISABLED;
355
356 if ( s->flags & SERVICE_USE_TIMEOUT )
357 {
358 long delta;
359 struct timeval curTime;
360 gettimeofday( &curTime, NULL );
361
362 delta = SERVICE_DiffTimeval( &s->expire, &curTime );
363 if ( delta > 0 )
364 SERVICE_AddTimeval( &s->expire,
365 (delta / s->rate) * s->rate );
366 }
367 }
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000368 retv = TRUE;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000369 break;
370 }
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000371 }
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000372
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000373 HeapUnlock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000374
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000375 QueueUserAPC( NULL, service_table->thread, 0L );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000376
377 return retv;
378}
379
380/***********************************************************************
381 * SERVICE_Disable
382 */
383BOOL SERVICE_Disable( HANDLE service )
384{
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000385 BOOL retv = TRUE;
386 SERVICE *s;
387 SERVICETABLE *service_table;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000388
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000389 /* service table must have been created on previous SERVICE_Add??? call */
390 if ((service_table = PROCESS_Current()->service_table) == 0)
391 return retv;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000392
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000393 HeapLock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000394
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000395 for ( s = service_table->first; s; s = s->next )
396 {
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000397 if ( s->self == service )
398 {
399 s->flags |= SERVICE_DISABLED;
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000400 retv = TRUE;
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000401 break;
402 }
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000403 }
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000404
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000405 HeapUnlock( GetProcessHeap() );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000406
Eric Pouech63c7cdf1999-06-12 08:24:23 +0000407 QueueUserAPC( NULL, service_table->thread, 0L );
Ulrich Weigand7761cbe1999-04-11 15:01:20 +0000408
409 return retv;
410}
411
412