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