Fixed function 0xb.
[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 "debugtools.h"
12
13 DEFAULT_DEBUG_CHANNEL(timer);
14
15 typedef struct _SERVICE
16 {
17    struct _SERVICE      *next;
18    HANDLE               self;
19
20    PAPCFUNC             callback;
21    ULONG_PTR            callback_arg;
22
23    BOOL                 disabled;
24    HANDLE               object;
25 } SERVICE;
26
27
28 static HANDLE service_thread;
29 static SERVICE *service_first;
30 static DWORD service_counter;
31  
32 /***********************************************************************
33  *           SERVICE_Loop
34  */
35 static DWORD CALLBACK SERVICE_Loop( void *dummy )
36 {
37     HANDLE      handles[MAXIMUM_WAIT_OBJECTS];
38     int         count = 0;
39     DWORD       retval = WAIT_FAILED;
40
41     while ( TRUE )
42     {
43         PAPCFUNC callback;
44         ULONG_PTR callback_arg;
45         SERVICE *s;
46
47         /* Check whether some condition is fulfilled */
48
49         HeapLock( GetProcessHeap() );
50
51         callback = NULL;
52         callback_arg = 0L;
53         for ( s = service_first; s; s = s->next )
54         {
55             if (s->disabled) continue;
56
57             if ( retval >= WAIT_OBJECT_0 && retval < WAIT_OBJECT_0 + count )
58             {
59                 if ( handles[retval - WAIT_OBJECT_0] == s->object )
60                 {
61                     retval = WAIT_TIMEOUT;
62                     callback = s->callback;
63                     callback_arg = s->callback_arg;
64                     break;
65                 }
66             }
67         }
68
69         HeapUnlock( GetProcessHeap() );
70         
71         /* If found, call callback routine */
72
73         if ( callback )
74         {
75             callback( callback_arg );
76             continue;
77         }
78
79         /* If not found, determine wait condition */
80
81         HeapLock( GetProcessHeap() );
82
83         count = 0;
84         for ( s = service_first; s; s = s->next )
85         {
86             if (s->disabled) continue;
87
88             if ( count < MAXIMUM_WAIT_OBJECTS )
89                 handles[count++] = s->object;
90         }
91
92         HeapUnlock( GetProcessHeap() );
93
94
95         /* Wait until some condition satisfied */
96
97         TRACE("Waiting for %d objects\n", count );
98
99         retval = WaitForMultipleObjectsEx( count, handles, FALSE, INFINITE, TRUE );
100
101         TRACE("Wait returned: %ld\n", retval );
102     }
103
104     return 0L;
105 }
106
107 /***********************************************************************
108  *           SERVICE_CreateServiceTable
109  */
110 static  BOOL    SERVICE_CreateServiceTable( void )
111 {
112     /* service_thread must be set *BEFORE* calling CreateThread
113      * otherwise the thread cleanup service will cause an infinite recursion
114      * when installed
115      */
116     service_thread = INVALID_HANDLE_VALUE;
117     service_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)SERVICE_Loop, 
118                                    NULL, 0, NULL );
119     return (service_thread != 0);
120 }
121
122 /***********************************************************************
123  *           SERVICE_AddObject
124  *
125  * Warning: the object supplied by the caller must not be closed. It'll
126  * be destroyed when the service is deleted. It's up to the caller
127  * to ensure that object will not be destroyed in between.
128  */
129 HANDLE SERVICE_AddObject( HANDLE object, 
130                           PAPCFUNC callback, ULONG_PTR callback_arg )
131 {
132     SERVICE             *s;
133     HANDLE              handle;
134
135     if ( !object || object == INVALID_HANDLE_VALUE || !callback ) 
136         return INVALID_HANDLE_VALUE;
137
138     if (!service_thread && !SERVICE_CreateServiceTable()) return INVALID_HANDLE_VALUE;
139
140     s = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SERVICE) );
141     if ( !s ) return INVALID_HANDLE_VALUE;
142
143     s->callback = callback;
144     s->callback_arg = callback_arg;
145     s->object = object;
146     s->disabled = FALSE;
147
148     HeapLock( GetProcessHeap() );
149
150     s->self = handle = (HANDLE)++service_counter;
151     s->next = service_first;
152     service_first = s;
153
154     HeapUnlock( GetProcessHeap() );
155
156     QueueUserAPC( NULL, service_thread, 0L );
157
158     return handle;
159 }
160
161 /***********************************************************************
162  *           SERVICE_AddTimer
163  */
164 HANDLE SERVICE_AddTimer( LONG rate, 
165                          PAPCFUNC callback, ULONG_PTR callback_arg )
166 {
167     HANDLE handle, ret;
168     LARGE_INTEGER when;
169
170     if ( !rate || !callback ) 
171         return INVALID_HANDLE_VALUE;
172
173     handle = CreateWaitableTimerA( NULL, FALSE, NULL );
174     if (!handle) return INVALID_HANDLE_VALUE;
175
176     if (!rate) rate = 1;
177     when.s.LowPart = when.s.HighPart = 0;
178     if (!SetWaitableTimer( handle, &when, rate, NULL, NULL, FALSE ))
179     {
180         CloseHandle( handle );
181         return INVALID_HANDLE_VALUE;
182     }
183
184     if ((ret = SERVICE_AddObject( handle, callback, callback_arg )) == INVALID_HANDLE_VALUE)
185     {
186         CloseHandle( handle );
187         return INVALID_HANDLE_VALUE;
188     }
189     return ret;
190 }
191
192 /***********************************************************************
193  *           SERVICE_Delete
194  */
195 BOOL SERVICE_Delete( HANDLE service )
196 {
197     HANDLE              handle = INVALID_HANDLE_VALUE;
198     BOOL                retv = FALSE;
199     SERVICE             **s, *next;
200
201     HeapLock( GetProcessHeap() );
202
203     for ( s = &service_first; *s; s = &(*s)->next )
204     {
205         if ( (*s)->self == service )
206         {
207             handle = (*s)->object;
208             next = (*s)->next;
209             HeapFree( GetProcessHeap(), 0, *s );
210             *s = next;
211             retv = TRUE;
212             break;
213         }
214     }
215
216     HeapUnlock( GetProcessHeap() );
217
218     if ( handle != INVALID_HANDLE_VALUE )
219         CloseHandle( handle );
220
221     QueueUserAPC( NULL, service_thread, 0L );
222
223     return retv;
224 }
225
226 /***********************************************************************
227  *           SERVICE_Enable
228  */
229 BOOL SERVICE_Enable( HANDLE service )
230 {
231     BOOL                retv = FALSE;
232     SERVICE             *s;
233
234     HeapLock( GetProcessHeap() );
235
236     for ( s = service_first; s; s = s->next ) 
237     {
238         if ( s->self == service )
239         {
240             s->disabled = FALSE;
241             retv = TRUE;
242             break;
243         }
244     }
245
246     HeapUnlock( GetProcessHeap() );
247
248     QueueUserAPC( NULL, service_thread, 0L );
249
250     return retv;
251 }
252
253 /***********************************************************************
254  *           SERVICE_Disable
255  */
256 BOOL SERVICE_Disable( HANDLE service )
257 {
258     BOOL                retv = TRUE;
259     SERVICE             *s;
260
261     HeapLock( GetProcessHeap() );
262
263     for ( s = service_first; s; s = s->next ) 
264     {
265         if ( s->self == service )
266         {
267             s->disabled = TRUE;
268             retv = TRUE;
269             break;
270         }
271     }
272
273     HeapUnlock( GetProcessHeap() );
274
275     QueueUserAPC( NULL, service_thread, 0L );
276
277     return retv;
278 }
279
280