- move async activation into the server
[wine] / scheduler / synchro.c
1 /*
2  * Win32 process and thread synchronisation
3  *
4  * Copyright 1997 Alexandre Julliard
5  */
6
7 #include <assert.h>
8 #include <errno.h>
9 #include <signal.h>
10 #include <sys/time.h>
11 #include <sys/poll.h>
12 #include <unistd.h>
13 #include <string.h>
14
15 #include "file.h"  /* for DOSFS_UnixTimeToFileTime */
16 #include "thread.h"
17 #include "winerror.h"
18 #include "wine/server.h"
19
20
21 /***********************************************************************
22  *              get_timeout
23  */
24 inline static void get_timeout( struct timeval *when, int timeout )
25 {
26     gettimeofday( when, 0 );
27     if (timeout)
28     {
29         long sec = timeout / 1000;
30         if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
31         {
32             when->tv_usec -= 1000000;
33             when->tv_sec++;
34         }
35         when->tv_sec += sec;
36     }
37 }
38
39 static void CALLBACK call_completion_routine(ULONG_PTR data)
40 {
41     async_private* ovp = (async_private*)data;
42
43     ovp->completion_func(ovp->lpOverlapped->Internal,
44                          ovp->lpOverlapped->InternalHigh,
45                          ovp->lpOverlapped);
46     ovp->completion_func=NULL;
47     HeapFree(GetProcessHeap(), 0, ovp);
48 }
49
50 static void finish_async(async_private *ovp, DWORD status)
51 {
52     ovp->lpOverlapped->Internal=status;
53
54     /* call ReadFileEx/WriteFileEx's overlapped completion function */
55     if(ovp->completion_func)
56     {
57         QueueUserAPC(call_completion_routine,GetCurrentThread(),(ULONG_PTR)ovp);
58     }
59
60     /* remove it from the active list */
61     if(ovp->prev)
62         ovp->prev->next = ovp->next;
63     else
64         NtCurrentTeb()->pending_list = ovp->next;
65
66     if(ovp->next)
67         ovp->next->prev = ovp->prev;
68
69     ovp->next=NULL;
70     ovp->prev=NULL;
71
72     close(ovp->fd);
73     NtSetEvent(ovp->lpOverlapped->hEvent,NULL);
74     if(!ovp->completion_func) HeapFree(GetProcessHeap(), 0, ovp);
75 }
76
77 /***********************************************************************
78  *           check_async_list
79  *
80  * Process a status event from the server.
81  */
82 void check_async_list(LPOVERLAPPED overlapped, DWORD status)
83             {
84     async_private *ovp;
85
86     /* fprintf(stderr,"overlapped %p status %x\n",overlapped,status); */
87
88     for(ovp = NtCurrentTeb()->pending_list; ovp; ovp = ovp->next)
89         if(ovp->lpOverlapped == overlapped)
90             break;
91
92     if(!ovp)
93             return;
94
95     if(status != STATUS_ALERTED)
96         ovp->lpOverlapped->Internal = status;
97
98     if(ovp->lpOverlapped->Internal==STATUS_PENDING)
99         {
100         ovp->func(ovp);
101         FILE_StartAsync(ovp->handle, ovp->lpOverlapped, ovp->type, 0, ovp->lpOverlapped->Internal);
102         }
103
104     if(ovp->lpOverlapped->Internal!=STATUS_PENDING)
105         finish_async(ovp,ovp->lpOverlapped->Internal);
106 }
107
108
109 /***********************************************************************
110  *              wait_reply
111  *
112  * Wait for a reply on the waiting pipe of the current thread.
113  */
114 static int wait_reply( void *cookie )
115 {
116     int signaled;
117     struct wake_up_reply reply;
118     for (;;)
119     {
120         int ret;
121         ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) );
122         if (ret == sizeof(reply))
123         {
124             if (!reply.cookie) break;  /* thread got killed */
125             if (reply.cookie == cookie) return reply.signaled;
126             /* we stole another reply, wait for the real one */
127             signaled = wait_reply( cookie );
128             /* and now put the wrong one back in the pipe */
129             for (;;)
130             {
131                 ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) );
132                 if (ret == sizeof(reply)) break;
133                 if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret );
134                 if (errno == EINTR) continue;
135                 server_protocol_perror("wakeup write");
136             }
137             return signaled;
138         }
139         if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret );
140         if (errno == EINTR) continue;
141         server_protocol_perror("wakeup read");
142     }
143     /* the server closed the connection; time to die... */
144     SYSDEPS_ExitThread(0);
145 }
146
147
148 /***********************************************************************
149  *              call_apcs
150  *
151  * Call outstanding APCs.
152  */
153 static void call_apcs( BOOL alertable )
154 {
155     FARPROC proc = NULL;
156     FILETIME ft;
157     void *args[4];
158
159     for (;;)
160     {
161         int type = APC_NONE;
162         SERVER_START_REQ( get_apc )
163         {
164             req->alertable = alertable;
165             wine_server_set_reply( req, args, sizeof(args) );
166             if (!wine_server_call( req ))
167             {
168                 type = reply->type;
169                 proc = reply->func;
170             }
171         }
172         SERVER_END_REQ;
173
174         switch(type)
175         {
176         case APC_NONE:
177             return;  /* no more APCs */
178         case APC_ASYNC:
179             proc( args[0], args[1]);
180             break;
181         case APC_USER:
182             proc( args[0] );
183             break;
184         case APC_TIMER:
185             /* convert sec/usec to NT time */
186             DOSFS_UnixTimeToFileTime( (time_t)args[0], &ft, (DWORD)args[1] * 10 );
187             proc( args[2], ft.dwLowDateTime, ft.dwHighDateTime );
188             break;
189         default:
190             server_protocol_error( "get_apc_request: bad type %d\n", type );
191             break;
192         }
193     }
194 }
195
196 /***********************************************************************
197  *              Sleep  (KERNEL32.@)
198  */
199 VOID WINAPI Sleep( DWORD timeout )
200 {
201     WaitForMultipleObjectsEx( 0, NULL, FALSE, timeout, FALSE );
202 }
203
204 /******************************************************************************
205  *              SleepEx   (KERNEL32.@)
206  */
207 DWORD WINAPI SleepEx( DWORD timeout, BOOL alertable )
208 {
209     DWORD ret = WaitForMultipleObjectsEx( 0, NULL, FALSE, timeout, alertable );
210     if (ret != WAIT_IO_COMPLETION) ret = 0;
211     return ret;
212 }
213
214
215 /***********************************************************************
216  *           WaitForSingleObject   (KERNEL32.@)
217  */
218 DWORD WINAPI WaitForSingleObject( HANDLE handle, DWORD timeout )
219 {
220     return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, FALSE );
221 }
222
223
224 /***********************************************************************
225  *           WaitForSingleObjectEx   (KERNEL32.@)
226  */
227 DWORD WINAPI WaitForSingleObjectEx( HANDLE handle, DWORD timeout,
228                                     BOOL alertable )
229 {
230     return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, alertable );
231 }
232
233
234 /***********************************************************************
235  *           WaitForMultipleObjects   (KERNEL32.@)
236  */
237 DWORD WINAPI WaitForMultipleObjects( DWORD count, const HANDLE *handles,
238                                      BOOL wait_all, DWORD timeout )
239 {
240     return WaitForMultipleObjectsEx( count, handles, wait_all, timeout, FALSE );
241 }
242
243
244 /***********************************************************************
245  *           WaitForMultipleObjectsEx   (KERNEL32.@)
246  */
247 DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
248                                        BOOL wait_all, DWORD timeout,
249                                        BOOL alertable )
250 {
251     int ret, cookie;
252     struct timeval tv;
253
254     if (count > MAXIMUM_WAIT_OBJECTS)
255     {
256         SetLastError( ERROR_INVALID_PARAMETER );
257         return WAIT_FAILED;
258     }
259
260     if (timeout == INFINITE) tv.tv_sec = tv.tv_usec = 0;
261     else get_timeout( &tv, timeout );
262
263     for (;;)
264     {
265         SERVER_START_REQ( select )
266         {
267             req->flags   = SELECT_INTERRUPTIBLE;
268             req->cookie  = &cookie;
269             req->sec     = tv.tv_sec;
270             req->usec    = tv.tv_usec;
271             wine_server_add_data( req, handles, count * sizeof(HANDLE) );
272
273             if (wait_all) req->flags |= SELECT_ALL;
274             if (alertable) req->flags |= SELECT_ALERTABLE;
275             if (timeout != INFINITE) req->flags |= SELECT_TIMEOUT;
276
277             ret = wine_server_call( req );
278         }
279         SERVER_END_REQ;
280         if (ret == STATUS_PENDING) ret = wait_reply( &cookie );
281         if (ret != STATUS_USER_APC) break;
282         call_apcs( alertable );
283         if (alertable) break;
284     }
285     if (HIWORD(ret))  /* is it an error code? */
286     {
287         SetLastError( RtlNtStatusToDosError(ret) );
288         ret = WAIT_FAILED;
289     }
290     return ret;
291 }
292
293
294 /***********************************************************************
295  *           WaitForSingleObject   (KERNEL.460)
296  */
297 DWORD WINAPI WaitForSingleObject16( HANDLE handle, DWORD timeout )
298 {
299     DWORD retval, mutex_count;
300
301     ReleaseThunkLock( &mutex_count );
302     retval = WaitForSingleObject( handle, timeout );
303     RestoreThunkLock( mutex_count );
304     return retval;
305 }
306
307 /***********************************************************************
308  *           WaitForMultipleObjects   (KERNEL.461)
309  */
310 DWORD WINAPI WaitForMultipleObjects16( DWORD count, const HANDLE *handles,
311                                        BOOL wait_all, DWORD timeout )
312 {
313     DWORD retval, mutex_count;
314
315     ReleaseThunkLock( &mutex_count );
316     retval = WaitForMultipleObjectsEx( count, handles, wait_all, timeout, FALSE );
317     RestoreThunkLock( mutex_count );
318     return retval;
319 }
320
321 /***********************************************************************
322  *           WaitForMultipleObjectsEx   (KERNEL.495)
323  */
324 DWORD WINAPI WaitForMultipleObjectsEx16( DWORD count, const HANDLE *handles,
325                                          BOOL wait_all, DWORD timeout, BOOL alertable )
326 {
327     DWORD retval, mutex_count;
328
329     ReleaseThunkLock( &mutex_count );
330     retval = WaitForMultipleObjectsEx( count, handles, wait_all, timeout, alertable );
331     RestoreThunkLock( mutex_count );
332     return retval;
333 }