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