Added (still incomplete) handling of MCI_BREAK command.
[wine/multimedia.git] / scheduler / services.c
blob68fc4d6b9418f13c6429e35a8a1b0fba0e59ae52
1 /*
2 * Kernel Services Thread
4 * Copyright 1999 Ulrich Weigand
5 */
7 #include <sys/time.h>
8 #include <unistd.h>
10 #include "services.h"
11 #include "debugtools.h"
13 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;
23 HANDLE self;
25 PAPCFUNC callback;
26 ULONG_PTR callback_arg;
28 int flags;
30 HANDLE object;
31 long rate;
33 struct timeval expire;
35 } SERVICE;
37 typedef struct
39 HANDLE heap;
40 HANDLE thread;
42 SERVICE *first;
43 DWORD counter;
45 } SERVICETABLE;
47 static SERVICETABLE *Service = NULL;
50 /***********************************************************************
51 * SERVICE_AddTimeval
53 static void SERVICE_AddTimeval( struct timeval *time, long delta )
55 delta += time->tv_usec;
56 time->tv_sec += delta / 1000000L;
57 time->tv_usec = delta % 1000000L;
60 /***********************************************************************
61 * SERVICE_DiffTimeval
63 static long SERVICE_DiffTimeval( struct timeval *time1, struct timeval *time2 )
65 return ( time1->tv_sec - time2->tv_sec ) * 1000000L
66 + ( time1->tv_usec - time2->tv_usec );
69 /***********************************************************************
70 * SERVICE_Loop
72 static DWORD CALLBACK SERVICE_Loop( SERVICETABLE *service )
74 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
75 int count = 0;
76 DWORD timeout = INFINITE;
77 DWORD retval = WAIT_FAILED;
79 while ( TRUE )
81 PAPCFUNC callback;
82 ULONG_PTR callback_arg;
83 SERVICE *s;
85 /* Check whether some condition is fulfilled */
87 struct timeval curTime;
88 gettimeofday( &curTime, NULL );
90 HeapLock( service->heap );
92 callback = NULL;
93 callback_arg = 0L;
94 for ( s = service->first; s; s = s->next )
96 if ( s->flags & SERVICE_DISABLED )
97 continue;
99 if ( s->flags & SERVICE_USE_OBJECT )
100 if ( retval >= WAIT_OBJECT_0 && retval < WAIT_OBJECT_0 + count )
101 if ( handles[retval - WAIT_OBJECT_0] == s->object )
103 retval = WAIT_TIMEOUT;
104 callback = s->callback;
105 callback_arg = s->callback_arg;
106 break;
109 if ( s->flags & SERVICE_USE_TIMEOUT )
110 if ((s->expire.tv_sec < curTime.tv_sec) ||
111 ((s->expire.tv_sec == curTime.tv_sec) &&
112 (s->expire.tv_usec <= curTime.tv_usec)))
114 SERVICE_AddTimeval( &s->expire, s->rate );
115 callback = s->callback;
116 callback_arg = s->callback_arg;
117 break;
121 HeapUnlock( service->heap );
124 /* If found, call callback routine */
126 if ( callback )
128 callback( callback_arg );
129 continue;
133 /* If not found, determine wait condition */
135 HeapLock( service->heap );
137 count = 0;
138 timeout = INFINITE;
139 for ( s = service->first; s; s = s->next )
141 if ( s->flags & SERVICE_DISABLED )
142 continue;
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( service->heap );
160 /* Wait until some condition satisfied */
162 TRACE("Waiting for %d objects with timeout %ld\n",
163 count, timeout );
165 retval = WaitForMultipleObjectsEx( count, handles,
166 FALSE, timeout, TRUE );
168 TRACE("Wait returned: %ld\n", retval );
171 return 0L;
174 /***********************************************************************
175 * SERVICE_Init
177 BOOL SERVICE_Init( void )
179 HANDLE heap, thread;
181 heap = HeapCreate( HEAP_GROWABLE, 0x1000, 0 );
182 if ( !heap ) return FALSE;
184 Service = HeapAlloc( heap, HEAP_ZERO_MEMORY, sizeof(SERVICETABLE) );
185 if ( !Service )
187 HeapDestroy( heap );
188 return FALSE;
191 Service->heap = heap;
193 thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)SERVICE_Loop,
194 Service, 0, NULL );
195 if ( thread == INVALID_HANDLE_VALUE )
197 HeapDestroy( heap );
198 return FALSE;
201 Service->thread = ConvertToGlobalHandle( thread );
203 return TRUE;
206 /***********************************************************************
207 * SERVICE_Exit
209 void SERVICE_Exit( void )
211 TerminateThread( Service->thread, 0 );
214 /***********************************************************************
215 * SERVICE_AddObject
217 HANDLE SERVICE_AddObject( HANDLE object,
218 PAPCFUNC callback, ULONG_PTR callback_arg )
220 SERVICE *s;
221 HANDLE handle;
223 object = ConvertToGlobalHandle( object ); /* FIXME */
225 if ( !Service || object == INVALID_HANDLE_VALUE || !callback )
226 return INVALID_HANDLE_VALUE;
228 s = HeapAlloc( Service->heap, HEAP_ZERO_MEMORY, sizeof(SERVICE) );
229 if ( !s ) return INVALID_HANDLE_VALUE;
231 HeapLock( Service->heap );
233 s->callback = callback;
234 s->callback_arg = callback_arg;
235 s->object = object;
236 s->flags = SERVICE_USE_OBJECT;
238 s->self = handle = (HANDLE)++Service->counter;
239 s->next = Service->first;
240 Service->first = s;
242 HeapUnlock( Service->heap );
244 QueueUserAPC( NULL, Service->thread, 0L );
246 return handle;
249 /***********************************************************************
250 * SERVICE_AddTimer
252 HANDLE SERVICE_AddTimer( LONG rate,
253 PAPCFUNC callback, ULONG_PTR callback_arg )
255 SERVICE *s;
256 HANDLE handle;
258 if ( !Service || !rate || !callback )
259 return INVALID_HANDLE_VALUE;
261 s = HeapAlloc( Service->heap, HEAP_ZERO_MEMORY, sizeof(SERVICE) );
262 if ( !s ) return INVALID_HANDLE_VALUE;
264 HeapLock( Service->heap );
266 s->callback = callback;
267 s->callback_arg = callback_arg;
268 s->rate = rate;
269 s->flags = SERVICE_USE_TIMEOUT;
271 gettimeofday( &s->expire, NULL );
272 SERVICE_AddTimeval( &s->expire, s->rate );
274 s->self = handle = (HANDLE)++Service->counter;
275 s->next = Service->first;
276 Service->first = s;
278 HeapUnlock( Service->heap );
280 QueueUserAPC( NULL, Service->thread, 0L );
282 return handle;
285 /***********************************************************************
286 * SERVICE_Delete
288 BOOL SERVICE_Delete( HANDLE service )
290 HANDLE handle = INVALID_HANDLE_VALUE;
291 BOOL retv = TRUE;
292 SERVICE **s, *next;
294 if ( !Service ) return retv;
296 HeapLock( Service->heap );
298 for ( s = &Service->first; *s; s = &(*s)->next )
299 if ( (*s)->self == service )
301 if ( (*s)->flags & SERVICE_USE_OBJECT )
302 handle = (*s)->object;
304 next = (*s)->next;
305 HeapFree( Service->heap, 0, *s );
306 *s = next;
307 retv = FALSE;
308 break;
311 HeapUnlock( Service->heap );
313 if ( handle != INVALID_HANDLE_VALUE )
314 CloseHandle( handle );
316 QueueUserAPC( NULL, Service->thread, 0L );
318 return retv;
321 /***********************************************************************
322 * SERVICE_Enable
324 BOOL SERVICE_Enable( HANDLE service )
326 BOOL retv = TRUE;
327 SERVICE *s;
329 if ( !Service ) return retv;
331 HeapLock( Service->heap );
333 for ( s = Service->first; s; s = s->next )
334 if ( s->self == service )
336 if ( s->flags & SERVICE_DISABLED )
338 s->flags &= ~SERVICE_DISABLED;
340 if ( s->flags & SERVICE_USE_TIMEOUT )
342 long delta;
343 struct timeval curTime;
344 gettimeofday( &curTime, NULL );
346 delta = SERVICE_DiffTimeval( &s->expire, &curTime );
347 if ( delta > 0 )
348 SERVICE_AddTimeval( &s->expire,
349 (delta / s->rate) * s->rate );
352 retv = FALSE;
353 break;
356 HeapUnlock( Service->heap );
358 QueueUserAPC( NULL, Service->thread, 0L );
360 return retv;
363 /***********************************************************************
364 * SERVICE_Disable
366 BOOL SERVICE_Disable( HANDLE service )
368 BOOL retv = TRUE;
369 SERVICE *s;
371 if ( !Service ) return retv;
373 HeapLock( Service->heap );
375 for ( s = Service->first; s; s = s->next )
376 if ( s->self == service )
378 s->flags |= SERVICE_DISABLED;
379 retv = FALSE;
380 break;
383 HeapUnlock( Service->heap );
385 QueueUserAPC( NULL, Service->thread, 0L );
387 return retv;