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