Do not use the PEB lock as loader lock, use a separate critical
[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 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     if(!ovp->completion_func) HeapFree(GetProcessHeap(), 0, ovp);
74 }
75
76 /***********************************************************************
77  *           check_async_list
78  *
79  * Process a status event from the server.
80  */
81 void WINAPI check_async_list(LPOVERLAPPED overlapped, DWORD status)
82 {
83     async_private *ovp;
84
85     /* fprintf(stderr,"overlapped %p status %x\n",overlapped,status); */
86
87     for(ovp = NtCurrentTeb()->pending_list; ovp; ovp = ovp->next)
88         if(ovp->lpOverlapped == overlapped)
89             break;
90
91     if(!ovp)
92             return;
93
94     if(status != STATUS_ALERTED)
95         ovp->lpOverlapped->Internal = status;
96
97     if(ovp->lpOverlapped->Internal==STATUS_PENDING)
98         {
99         ovp->func(ovp);
100         FILE_StartAsync(ovp->handle, ovp->lpOverlapped, ovp->type, 0, ovp->lpOverlapped->Internal);
101         }
102
103     if(ovp->lpOverlapped->Internal!=STATUS_PENDING)
104         finish_async(ovp,ovp->lpOverlapped->Internal);
105 }
106
107
108 /***********************************************************************
109  *              wait_reply
110  *
111  * Wait for a reply on the waiting pipe of the current thread.
112  */
113 static int wait_reply( void *cookie )
114 {
115     int signaled;
116     struct wake_up_reply reply;
117     for (;;)
118     {
119         int ret;
120         ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) );
121         if (ret == sizeof(reply))
122         {
123             if (!reply.cookie) break;  /* thread got killed */
124             if (reply.cookie == cookie) return reply.signaled;
125             /* we stole another reply, wait for the real one */
126             signaled = wait_reply( cookie );
127             /* and now put the wrong one back in the pipe */
128             for (;;)
129             {
130                 ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) );
131                 if (ret == sizeof(reply)) break;
132                 if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret );
133                 if (errno == EINTR) continue;
134                 server_protocol_perror("wakeup write");
135             }
136             return signaled;
137         }
138         if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret );
139         if (errno == EINTR) continue;
140         server_protocol_perror("wakeup read");
141     }
142     /* the server closed the connection; time to die... */
143     SYSDEPS_AbortThread(0);
144 }
145
146
147 /***********************************************************************
148  *              call_apcs
149  *
150  * Call outstanding APCs.
151  */
152 static void call_apcs( BOOL alertable )
153 {
154     FARPROC proc = NULL;
155     FILETIME ft;
156     void *args[4];
157
158     for (;;)
159     {
160         int type = APC_NONE;
161         SERVER_START_REQ( get_apc )
162         {
163             req->alertable = alertable;
164             wine_server_set_reply( req, args, sizeof(args) );
165             if (!wine_server_call( req ))
166             {
167                 type = reply->type;
168                 proc = reply->func;
169             }
170         }
171         SERVER_END_REQ;
172
173         switch(type)
174         {
175         case APC_NONE:
176             return;  /* no more APCs */
177         case APC_ASYNC:
178             proc( args[0], args[1]);
179             break;
180         case APC_USER:
181             proc( args[0] );
182             break;
183         case APC_TIMER:
184             /* convert sec/usec to NT time */
185             DOSFS_UnixTimeToFileTime( (time_t)args[0], &ft, (DWORD)args[1] * 10 );
186             proc( args[2], ft.dwLowDateTime, ft.dwHighDateTime );
187             break;
188         default:
189             server_protocol_error( "get_apc_request: bad type %d\n", type );
190             break;
191         }
192     }
193 }
194
195 /***********************************************************************
196  *              Sleep  (KERNEL32.@)
197  */
198 VOID WINAPI Sleep( DWORD timeout )
199 {
200     WaitForMultipleObjectsEx( 0, NULL, FALSE, timeout, FALSE );
201 }
202
203 /******************************************************************************
204  *              SleepEx   (KERNEL32.@)
205  */
206 DWORD WINAPI SleepEx( DWORD timeout, BOOL alertable )
207 {
208     DWORD ret = WaitForMultipleObjectsEx( 0, NULL, FALSE, timeout, alertable );
209     if (ret != WAIT_IO_COMPLETION) ret = 0;
210     return ret;
211 }
212
213
214 /***********************************************************************
215  *           WaitForSingleObject   (KERNEL32.@)
216  */
217 DWORD WINAPI WaitForSingleObject( HANDLE handle, DWORD timeout )
218 {
219     return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, FALSE );
220 }
221
222
223 /***********************************************************************
224  *           WaitForSingleObjectEx   (KERNEL32.@)
225  */
226 DWORD WINAPI WaitForSingleObjectEx( HANDLE handle, DWORD timeout,
227                                     BOOL alertable )
228 {
229     return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, alertable );
230 }
231
232
233 /***********************************************************************
234  *           WaitForMultipleObjects   (KERNEL32.@)
235  */
236 DWORD WINAPI WaitForMultipleObjects( DWORD count, const HANDLE *handles,
237                                      BOOL wait_all, DWORD timeout )
238 {
239     return WaitForMultipleObjectsEx( count, handles, wait_all, timeout, FALSE );
240 }
241
242
243 /***********************************************************************
244  *           WaitForMultipleObjectsEx   (KERNEL32.@)
245  */
246 DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
247                                        BOOL wait_all, DWORD timeout,
248                                        BOOL alertable )
249 {
250     int ret, cookie;
251     struct timeval tv;
252
253     if (count > MAXIMUM_WAIT_OBJECTS)
254     {
255         SetLastError( ERROR_INVALID_PARAMETER );
256         return WAIT_FAILED;
257     }
258
259     if (timeout == INFINITE) tv.tv_sec = tv.tv_usec = 0;
260     else get_timeout( &tv, timeout );
261
262     for (;;)
263     {
264         SERVER_START_REQ( select )
265         {
266             req->flags   = SELECT_INTERRUPTIBLE;
267             req->cookie  = &cookie;
268             req->sec     = tv.tv_sec;
269             req->usec    = tv.tv_usec;
270             wine_server_add_data( req, handles, count * sizeof(HANDLE) );
271
272             if (wait_all) req->flags |= SELECT_ALL;
273             if (alertable) req->flags |= SELECT_ALERTABLE;
274             if (timeout != INFINITE) req->flags |= SELECT_TIMEOUT;
275
276             ret = wine_server_call( req );
277         }
278         SERVER_END_REQ;
279         if (ret == STATUS_PENDING) ret = wait_reply( &cookie );
280         if (ret != STATUS_USER_APC) break;
281         call_apcs( alertable );
282         if (alertable) break;
283     }
284     if (HIWORD(ret))  /* is it an error code? */
285     {
286         SetLastError( RtlNtStatusToDosError(ret) );
287         ret = WAIT_FAILED;
288     }
289     return ret;
290 }
291
292
293 /***********************************************************************
294  *           WaitForSingleObject   (KERNEL.460)
295  */
296 DWORD WINAPI WaitForSingleObject16( HANDLE handle, DWORD timeout )
297 {
298     DWORD retval, mutex_count;
299
300     ReleaseThunkLock( &mutex_count );
301     retval = WaitForSingleObject( handle, timeout );
302     RestoreThunkLock( mutex_count );
303     return retval;
304 }
305
306 /***********************************************************************
307  *           WaitForMultipleObjects   (KERNEL.461)
308  */
309 DWORD WINAPI WaitForMultipleObjects16( DWORD count, const HANDLE *handles,
310                                        BOOL wait_all, DWORD timeout )
311 {
312     DWORD retval, mutex_count;
313
314     ReleaseThunkLock( &mutex_count );
315     retval = WaitForMultipleObjectsEx( count, handles, wait_all, timeout, FALSE );
316     RestoreThunkLock( mutex_count );
317     return retval;
318 }
319
320 /***********************************************************************
321  *           WaitForMultipleObjectsEx   (KERNEL.495)
322  */
323 DWORD WINAPI WaitForMultipleObjectsEx16( DWORD count, const HANDLE *handles,
324                                          BOOL wait_all, DWORD timeout, BOOL alertable )
325 {
326     DWORD retval, mutex_count;
327
328     ReleaseThunkLock( &mutex_count );
329     retval = WaitForMultipleObjectsEx( count, handles, wait_all, timeout, alertable );
330     RestoreThunkLock( mutex_count );
331     return retval;
332 }