- separate cleanly between async scheduling and file IO related issues.
[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 #include "async.h"
34
35
36 /***********************************************************************
37  *              get_timeout
38  */
39 inline static void get_timeout( struct timeval *when, int timeout )
40 {
41     gettimeofday( when, 0 );
42     if (timeout)
43     {
44         long sec = timeout / 1000;
45         if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
46         {
47             when->tv_usec -= 1000000;
48             when->tv_sec++;
49         }
50         when->tv_sec += sec;
51     }
52 }
53
54 /***********************************************************************
55  *           check_async_list
56  *
57  * Process a status event from the server.
58  */
59 static void WINAPI check_async_list(async_private *asp, DWORD status)
60 {
61     async_private *ovp;
62     DWORD ovp_status;
63
64     for( ovp = NtCurrentTeb()->pending_list; ovp && ovp != asp; ovp = ovp->next );
65
66     if(!ovp)
67             return;
68
69     if( status != STATUS_ALERTED )
70     {
71         ovp_status = status;
72         ovp->ops->set_status (ovp, status);
73     }
74     else ovp_status = ovp->ops->get_status (ovp);
75
76     if( ovp_status == STATUS_PENDING ) ovp->func( ovp );
77
78     /* This will destroy all but PENDING requests */
79     register_old_async( ovp );
80 }
81
82
83 /***********************************************************************
84  *              wait_reply
85  *
86  * Wait for a reply on the waiting pipe of the current thread.
87  */
88 static int wait_reply( void *cookie )
89 {
90     int signaled;
91     struct wake_up_reply reply;
92     for (;;)
93     {
94         int ret;
95         ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) );
96         if (ret == sizeof(reply))
97         {
98             if (!reply.cookie) break;  /* thread got killed */
99             if (reply.cookie == cookie) return reply.signaled;
100             /* we stole another reply, wait for the real one */
101             signaled = wait_reply( cookie );
102             /* and now put the wrong one back in the pipe */
103             for (;;)
104             {
105                 ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) );
106                 if (ret == sizeof(reply)) break;
107                 if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret );
108                 if (errno == EINTR) continue;
109                 server_protocol_perror("wakeup write");
110             }
111             return signaled;
112         }
113         if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret );
114         if (errno == EINTR) continue;
115         server_protocol_perror("wakeup read");
116     }
117     /* the server closed the connection; time to die... */
118     SYSDEPS_AbortThread(0);
119 }
120
121
122 /***********************************************************************
123  *              call_apcs
124  *
125  * Call outstanding APCs.
126  */
127 static void call_apcs( BOOL alertable )
128 {
129     FARPROC proc = NULL;
130     FILETIME ft;
131     void *args[4];
132
133     for (;;)
134     {
135         int type = APC_NONE;
136         SERVER_START_REQ( get_apc )
137         {
138             req->alertable = alertable;
139             wine_server_set_reply( req, args, sizeof(args) );
140             if (!wine_server_call( req ))
141             {
142                 type = reply->type;
143                 proc = reply->func;
144             }
145         }
146         SERVER_END_REQ;
147
148         switch(type)
149         {
150         case APC_NONE:
151             return;  /* no more APCs */
152         case APC_ASYNC:
153             proc( args[0], args[1]);
154             break;
155         case APC_USER:
156             proc( args[0] );
157             break;
158         case APC_TIMER:
159             /* convert sec/usec to NT time */
160             DOSFS_UnixTimeToFileTime( (time_t)args[0], &ft, (DWORD)args[1] * 10 );
161             proc( args[2], ft.dwLowDateTime, ft.dwHighDateTime );
162             break;
163         case APC_ASYNC_IO:
164             check_async_list ( args[0], (DWORD) args[1]);
165             break;
166         default:
167             server_protocol_error( "get_apc_request: bad type %d\n", type );
168             break;
169         }
170     }
171 }
172
173 /***********************************************************************
174  *              Sleep  (KERNEL32.@)
175  */
176 VOID WINAPI Sleep( DWORD timeout )
177 {
178     WaitForMultipleObjectsEx( 0, NULL, FALSE, timeout, FALSE );
179 }
180
181 /******************************************************************************
182  *              SleepEx   (KERNEL32.@)
183  */
184 DWORD WINAPI SleepEx( DWORD timeout, BOOL alertable )
185 {
186     DWORD ret = WaitForMultipleObjectsEx( 0, NULL, FALSE, timeout, alertable );
187     if (ret != WAIT_IO_COMPLETION) ret = 0;
188     return ret;
189 }
190
191
192 /***********************************************************************
193  *           WaitForSingleObject   (KERNEL32.@)
194  */
195 DWORD WINAPI WaitForSingleObject( HANDLE handle, DWORD timeout )
196 {
197     return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, FALSE );
198 }
199
200
201 /***********************************************************************
202  *           WaitForSingleObjectEx   (KERNEL32.@)
203  */
204 DWORD WINAPI WaitForSingleObjectEx( HANDLE handle, DWORD timeout,
205                                     BOOL alertable )
206 {
207     return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, alertable );
208 }
209
210
211 /***********************************************************************
212  *           WaitForMultipleObjects   (KERNEL32.@)
213  */
214 DWORD WINAPI WaitForMultipleObjects( DWORD count, const HANDLE *handles,
215                                      BOOL wait_all, DWORD timeout )
216 {
217     return WaitForMultipleObjectsEx( count, handles, wait_all, timeout, FALSE );
218 }
219
220
221 /***********************************************************************
222  *           WaitForMultipleObjectsEx   (KERNEL32.@)
223  */
224 DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
225                                        BOOL wait_all, DWORD timeout,
226                                        BOOL alertable )
227 {
228     int ret, cookie;
229     struct timeval tv;
230
231     if (count > MAXIMUM_WAIT_OBJECTS)
232     {
233         SetLastError( ERROR_INVALID_PARAMETER );
234         return WAIT_FAILED;
235     }
236
237     if (timeout == INFINITE) tv.tv_sec = tv.tv_usec = 0;
238     else get_timeout( &tv, timeout );
239
240     for (;;)
241     {
242         SERVER_START_REQ( select )
243         {
244             req->flags   = SELECT_INTERRUPTIBLE;
245             req->cookie  = &cookie;
246             req->sec     = tv.tv_sec;
247             req->usec    = tv.tv_usec;
248             wine_server_add_data( req, handles, count * sizeof(HANDLE) );
249
250             if (wait_all) req->flags |= SELECT_ALL;
251             if (alertable) req->flags |= SELECT_ALERTABLE;
252             if (timeout != INFINITE) req->flags |= SELECT_TIMEOUT;
253
254             ret = wine_server_call( req );
255         }
256         SERVER_END_REQ;
257         if (ret == STATUS_PENDING) ret = wait_reply( &cookie );
258         if (ret != STATUS_USER_APC) break;
259         call_apcs( alertable );
260         if (alertable) break;
261     }
262     if (HIWORD(ret))  /* is it an error code? */
263     {
264         SetLastError( RtlNtStatusToDosError(ret) );
265         ret = WAIT_FAILED;
266     }
267     return ret;
268 }
269
270
271 /***********************************************************************
272  *           WaitForSingleObject   (KERNEL.460)
273  */
274 DWORD WINAPI WaitForSingleObject16( HANDLE handle, DWORD timeout )
275 {
276     DWORD retval, mutex_count;
277
278     ReleaseThunkLock( &mutex_count );
279     retval = WaitForSingleObject( handle, timeout );
280     RestoreThunkLock( mutex_count );
281     return retval;
282 }
283
284 /***********************************************************************
285  *           WaitForMultipleObjects   (KERNEL.461)
286  */
287 DWORD WINAPI WaitForMultipleObjects16( DWORD count, const HANDLE *handles,
288                                        BOOL wait_all, DWORD timeout )
289 {
290     DWORD retval, mutex_count;
291
292     ReleaseThunkLock( &mutex_count );
293     retval = WaitForMultipleObjectsEx( count, handles, wait_all, timeout, FALSE );
294     RestoreThunkLock( mutex_count );
295     return retval;
296 }
297
298 /***********************************************************************
299  *           WaitForMultipleObjectsEx   (KERNEL.495)
300  */
301 DWORD WINAPI WaitForMultipleObjectsEx16( DWORD count, const HANDLE *handles,
302                                          BOOL wait_all, DWORD timeout, BOOL alertable )
303 {
304     DWORD retval, mutex_count;
305
306     ReleaseThunkLock( &mutex_count );
307     retval = WaitForMultipleObjectsEx( count, handles, wait_all, timeout, alertable );
308     RestoreThunkLock( mutex_count );
309     return retval;
310 }