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 );