Added WNetRemoveCachedPassword() stub.
[wine] / scheduler / services.c
1 /*
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"
11 #include "process.h"
12 #include "debugtools.h"
13
14 DEFAULT_DEBUG_CHANNEL(timer)
15
16 #define SERVICE_USE_OBJECT      0x0001
17 #define SERVICE_USE_TIMEOUT     0x0002
18 #define SERVICE_DISABLED        0x8000
19
20 typedef struct _SERVICE
21 {
22    struct _SERVICE      *next;
23    HANDLE               self;
24
25    PAPCFUNC             callback;
26    ULONG_PTR            callback_arg;
27
28    int                  flags;
29
30    HANDLE               object;
31    long                 rate;
32
33    struct timeval       expire;
34
35 } SERVICE;
36
37 typedef struct _SERVICETABLE
38 {
39    HANDLE               thread;
40
41    SERVICE              *first;
42    DWORD                counter;
43
44 } SERVICETABLE;
45  
46 /***********************************************************************
47  *           SERVICE_AddTimeval
48  */
49 static 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  */
59 static 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  */
68 static DWORD CALLBACK SERVICE_Loop( SERVICETABLE *service )
69 {
70     HANDLE      handles[MAXIMUM_WAIT_OBJECTS];
71     int         count = 0;
72     DWORD       timeout = INFINITE;
73     DWORD       retval = WAIT_FAILED;
74
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
86         HeapLock( GetProcessHeap() );
87
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
95             if ( s->flags & SERVICE_USE_OBJECT ) 
96             {
97                 if ( retval >= WAIT_OBJECT_0 && retval < WAIT_OBJECT_0 + count )
98                 {
99                     if ( handles[retval - WAIT_OBJECT_0] == s->object )
100                     {
101                         retval = WAIT_TIMEOUT;
102                         callback = s->callback;
103                         callback_arg = s->callback_arg;
104                         break;
105                     }
106                 }
107             }
108
109             if ( s->flags & SERVICE_USE_TIMEOUT )
110             {
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)))
114                 {
115                     SERVICE_AddTimeval( &s->expire, s->rate );
116                     callback = s->callback;
117                     callback_arg = s->callback_arg;
118                     break;
119                 }
120             }
121         }
122
123         HeapUnlock( GetProcessHeap() );
124         
125         /* If found, call callback routine */
126
127         if ( callback )
128         {
129             callback( callback_arg );
130             continue;
131         }
132
133         /* If not found, determine wait condition */
134
135         HeapLock( GetProcessHeap() );
136
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 )
145                 if ( count < MAXIMUM_WAIT_OBJECTS )
146                     handles[count++] = s->object;
147
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
157         HeapUnlock( GetProcessHeap() );
158
159
160         /* Wait until some condition satisfied */
161
162         TRACE("Waiting for %d objects with timeout %ld\n", 
163                       count, timeout );
164
165         retval = WaitForMultipleObjectsEx( count, handles, 
166                                            FALSE, timeout, TRUE );
167
168         TRACE("Wait returned: %ld\n", retval );
169     }
170
171     return 0L;
172 }
173
174 /***********************************************************************
175  *           SERVICE_CreateServiceTable
176  */
177 static  BOOL    SERVICE_CreateServiceTable( void )
178 {
179     HANDLE              thread;
180     SERVICETABLE        *service_table;
181     PDB                 *pdb = PROCESS_Current();
182
183     service_table = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICETABLE) );
184     if ( !service_table )
185     {
186         return FALSE;
187     }
188
189     /* 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;
194
195     thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)SERVICE_Loop, 
196                            service_table, 0, NULL );
197     if ( thread == INVALID_HANDLE_VALUE )
198     {
199         pdb->service_table = 0;
200         HeapFree( GetProcessHeap(), 0, service_table );
201         return FALSE;
202     }
203     
204     service_table->thread = thread;
205
206     return TRUE;
207 }
208
209 /***********************************************************************
210  *           SERVICE_AddObject
211  *
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.
215  */
216 HANDLE SERVICE_AddObject( HANDLE object, 
217                           PAPCFUNC callback, ULONG_PTR callback_arg )
218 {
219     SERVICE             *s;
220     SERVICETABLE        *service_table;
221     HANDLE              handle;
222
223     if ( object == INVALID_HANDLE_VALUE || !callback ) 
224         return INVALID_HANDLE_VALUE;
225
226     if (PROCESS_Current()->service_table == 0 && !SERVICE_CreateServiceTable())
227        return INVALID_HANDLE_VALUE;
228     service_table = PROCESS_Current()->service_table;
229
230     s = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) );
231     if ( !s ) return INVALID_HANDLE_VALUE;
232
233     s->callback = callback;
234     s->callback_arg = callback_arg;
235     s->object = object;
236     s->flags = SERVICE_USE_OBJECT;
237
238     HeapLock( GetProcessHeap() );
239
240     s->self = handle = (HANDLE)++service_table->counter;
241     s->next = service_table->first;
242     service_table->first = s;
243
244     HeapUnlock( GetProcessHeap() );
245
246     QueueUserAPC( NULL, service_table->thread, 0L );
247
248     return handle;
249 }
250
251 /***********************************************************************
252  *           SERVICE_AddTimer
253  */
254 HANDLE SERVICE_AddTimer( LONG rate, 
255                          PAPCFUNC callback, ULONG_PTR callback_arg )
256 {
257     SERVICE             *s;
258     SERVICETABLE        *service_table;
259     HANDLE              handle;
260
261     if ( !rate || !callback ) 
262         return INVALID_HANDLE_VALUE;
263
264     if (PROCESS_Current()->service_table == 0 && !SERVICE_CreateServiceTable())
265        return INVALID_HANDLE_VALUE;
266     service_table = PROCESS_Current()->service_table;
267
268     s = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) );
269     if ( !s ) return INVALID_HANDLE_VALUE;
270
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             
279     HeapLock( GetProcessHeap() );
280
281     s->self = handle = (HANDLE)++service_table->counter;
282     s->next = service_table->first;
283     service_table->first = s;
284
285     HeapUnlock( GetProcessHeap() );
286
287     QueueUserAPC( NULL, service_table->thread, 0L );
288
289     return handle;
290 }
291
292 /***********************************************************************
293  *           SERVICE_Delete
294  */
295 BOOL SERVICE_Delete( HANDLE service )
296 {
297     HANDLE              handle = INVALID_HANDLE_VALUE;
298     BOOL                retv = FALSE;
299     SERVICE             **s, *next;
300     SERVICETABLE        *service_table;
301
302     /* service table must have been created on previous SERVICE_Add??? call */
303     if ((service_table = PROCESS_Current()->service_table) == 0)
304        return retv;
305
306     HeapLock( GetProcessHeap() );
307
308     for ( s = &service_table->first; *s; s = &(*s)->next )
309     {
310         if ( (*s)->self == service )
311         {
312             if ( (*s)->flags & SERVICE_USE_OBJECT )
313                 handle = (*s)->object;
314
315             next = (*s)->next;
316             HeapFree( GetProcessHeap(), 0, *s );
317             *s = next;
318             retv = TRUE;
319             break;
320         }
321     }
322
323     HeapUnlock( GetProcessHeap() );
324
325     if ( handle != INVALID_HANDLE_VALUE )
326         CloseHandle( handle );
327
328     QueueUserAPC( NULL, service_table->thread, 0L );
329
330     return retv;
331 }
332
333 /***********************************************************************
334  *           SERVICE_Enable
335  */
336 BOOL SERVICE_Enable( HANDLE service )
337 {
338     BOOL                retv = FALSE;
339     SERVICE             *s;
340     SERVICETABLE        *service_table;
341
342     /* service table must have been created on previous SERVICE_Add??? call */
343     if ((service_table = PROCESS_Current()->service_table) == 0)
344        return retv;
345
346     HeapLock( GetProcessHeap() );
347
348     for ( s = service_table->first; s; s = s->next ) 
349     {
350         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             }
368             retv = TRUE;
369             break;
370         }
371     }
372
373     HeapUnlock( GetProcessHeap() );
374
375     QueueUserAPC( NULL, service_table->thread, 0L );
376
377     return retv;
378 }
379
380 /***********************************************************************
381  *           SERVICE_Disable
382  */
383 BOOL SERVICE_Disable( HANDLE service )
384 {
385     BOOL                retv = TRUE;
386     SERVICE             *s;
387     SERVICETABLE        *service_table;
388
389     /* service table must have been created on previous SERVICE_Add??? call */
390     if ((service_table = PROCESS_Current()->service_table) == 0)
391        return retv;
392
393     HeapLock( GetProcessHeap() );
394
395     for ( s = service_table->first; s; s = s->next ) 
396     {
397         if ( s->self == service )
398         {
399             s->flags |= SERVICE_DISABLED;
400             retv = TRUE;
401             break;
402         }
403     }
404
405     HeapUnlock( GetProcessHeap() );
406
407     QueueUserAPC( NULL, service_table->thread, 0L );
408
409     return retv;
410 }
411
412