Implemented NtQueueApcThread, and changed the server APC interface to
[wine] / dlls / ntdll / sync.c
1 /*
2  *      Process synchronisation
3  *
4  * Copyright 1997 Alexandre Julliard
5  * Copyright 1999, 2000 Juergen Schmied
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23
24 #include <assert.h>
25 #include <errno.h>
26 #include <signal.h>
27 #ifdef HAVE_SYS_TIME_H
28 # include <sys/time.h>
29 #endif
30 #ifdef HAVE_SYS_POLL_H
31 # include <sys/poll.h>
32 #endif
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 #include <string.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <time.h>
40
41 #define NONAMELESSUNION
42 #define NONAMELESSSTRUCT
43
44 #include "winternl.h"
45 #include "async.h"
46 #include "thread.h"
47 #include "wine/server.h"
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
50 #include "ntdll_misc.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
53
54
55 /*
56  *      Semaphores
57  */
58
59 /******************************************************************************
60  *  NtCreateSemaphore (NTDLL.@)
61  */
62 NTSTATUS WINAPI NtCreateSemaphore( OUT PHANDLE SemaphoreHandle,
63                                    IN ACCESS_MASK access,
64                                    IN const OBJECT_ATTRIBUTES *attr OPTIONAL,
65                                    IN ULONG InitialCount,
66                                    IN ULONG MaximumCount )
67 {
68     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
69     NTSTATUS ret;
70
71     if ((MaximumCount <= 0) || (InitialCount > MaximumCount))
72         return STATUS_INVALID_PARAMETER;
73
74     SERVER_START_REQ( create_semaphore )
75     {
76         req->initial = InitialCount;
77         req->max     = MaximumCount;
78         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
79         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
80         ret = wine_server_call( req );
81         *SemaphoreHandle = reply->handle;
82     }
83     SERVER_END_REQ;
84     return ret;
85 }
86
87 /******************************************************************************
88  *  NtOpenSemaphore (NTDLL.@)
89  */
90 NTSTATUS WINAPI NtOpenSemaphore( OUT PHANDLE SemaphoreHandle,
91                                  IN ACCESS_MASK access,
92                                  IN const OBJECT_ATTRIBUTES *attr )
93 {
94     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
95     NTSTATUS ret;
96
97     SERVER_START_REQ( open_semaphore )
98     {
99         req->access  = access;
100         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
101         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
102         ret = wine_server_call( req );
103         *SemaphoreHandle = reply->handle;
104     }
105     SERVER_END_REQ;
106     return ret;
107 }
108
109 /******************************************************************************
110  *  NtQuerySemaphore (NTDLL.@)
111  */
112 NTSTATUS WINAPI NtQuerySemaphore(
113         HANDLE SemaphoreHandle,
114         PVOID SemaphoreInformationClass,
115         OUT PVOID SemaphoreInformation,
116         ULONG Length,
117         PULONG ReturnLength)
118 {
119         FIXME("(%p,%p,%p,0x%08lx,%p) stub!\n",
120         SemaphoreHandle, SemaphoreInformationClass, SemaphoreInformation, Length, ReturnLength);
121         return STATUS_SUCCESS;
122 }
123
124 /******************************************************************************
125  *  NtReleaseSemaphore (NTDLL.@)
126  */
127 NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous )
128 {
129     NTSTATUS ret;
130     SERVER_START_REQ( release_semaphore )
131     {
132         req->handle = handle;
133         req->count  = count;
134         if (!(ret = wine_server_call( req )))
135         {
136             if (previous) *previous = reply->prev_count;
137         }
138     }
139     SERVER_END_REQ;
140     return ret;
141 }
142
143 /*
144  *      Events
145  */
146
147 /**************************************************************************
148  * NtCreateEvent (NTDLL.@)
149  * ZwCreateEvent (NTDLL.@)
150  */
151 NTSTATUS WINAPI NtCreateEvent(
152         OUT PHANDLE EventHandle,
153         IN ACCESS_MASK DesiredAccess,
154         IN const OBJECT_ATTRIBUTES *attr,
155         IN BOOLEAN ManualReset,
156         IN BOOLEAN InitialState)
157 {
158     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
159     NTSTATUS ret;
160
161     SERVER_START_REQ( create_event )
162     {
163         req->manual_reset = ManualReset;
164         req->initial_state = InitialState;
165         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
166         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
167         ret = wine_server_call( req );
168         *EventHandle = reply->handle;
169     }
170     SERVER_END_REQ;
171     return ret;
172 }
173
174 /******************************************************************************
175  *  NtOpenEvent (NTDLL.@)
176  *  ZwOpenEvent (NTDLL.@)
177  */
178 NTSTATUS WINAPI NtOpenEvent(
179         OUT PHANDLE EventHandle,
180         IN ACCESS_MASK DesiredAccess,
181         IN const OBJECT_ATTRIBUTES *attr )
182 {
183     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
184     NTSTATUS ret;
185
186     SERVER_START_REQ( open_event )
187     {
188         req->access  = DesiredAccess;
189         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
190         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
191         ret = wine_server_call( req );
192         *EventHandle = reply->handle;
193     }
194     SERVER_END_REQ;
195     return ret;
196 }
197
198
199 /******************************************************************************
200  *  NtSetEvent (NTDLL.@)
201  *  ZwSetEvent (NTDLL.@)
202  */
203 NTSTATUS WINAPI NtSetEvent( HANDLE handle, PULONG NumberOfThreadsReleased )
204 {
205     NTSTATUS ret;
206
207     /* FIXME: set NumberOfThreadsReleased */
208
209     SERVER_START_REQ( event_op )
210     {
211         req->handle = handle;
212         req->op     = SET_EVENT;
213         ret = wine_server_call( req );
214     }
215     SERVER_END_REQ;
216     return ret;
217 }
218
219 /******************************************************************************
220  *  NtResetEvent (NTDLL.@)
221  */
222 NTSTATUS WINAPI NtResetEvent( HANDLE handle, PULONG NumberOfThreadsReleased )
223 {
224     NTSTATUS ret;
225
226     /* resetting an event can't release any thread... */
227     if (NumberOfThreadsReleased) *NumberOfThreadsReleased = 0;
228
229     SERVER_START_REQ( event_op )
230     {
231         req->handle = handle;
232         req->op     = RESET_EVENT;
233         ret = wine_server_call( req );
234     }
235     SERVER_END_REQ;
236     return ret;
237 }
238
239 /******************************************************************************
240  *  NtClearEvent (NTDLL.@)
241  *
242  * FIXME
243  *   same as NtResetEvent ???
244  */
245 NTSTATUS WINAPI NtClearEvent ( HANDLE handle )
246 {
247     return NtResetEvent( handle, NULL );
248 }
249
250 /******************************************************************************
251  *  NtPulseEvent (NTDLL.@)
252  *
253  * FIXME
254  *   PulseCount
255  */
256 NTSTATUS WINAPI NtPulseEvent( HANDLE handle, PULONG PulseCount )
257 {
258     NTSTATUS ret;
259     FIXME("(%p,%p)\n", handle, PulseCount);
260     SERVER_START_REQ( event_op )
261     {
262         req->handle = handle;
263         req->op     = PULSE_EVENT;
264         ret = wine_server_call( req );
265     }
266     SERVER_END_REQ;
267     return ret;
268 }
269
270 /******************************************************************************
271  *  NtQueryEvent (NTDLL.@)
272  */
273 NTSTATUS WINAPI NtQueryEvent (
274         IN  HANDLE EventHandle,
275         IN  UINT EventInformationClass,
276         OUT PVOID EventInformation,
277         IN  ULONG EventInformationLength,
278         OUT PULONG  ReturnLength)
279 {
280         FIXME("(%p)\n", EventHandle);
281         return STATUS_SUCCESS;
282 }
283
284
285 /***********************************************************************
286  *           check_async_list
287  *
288  * Process a status event from the server.
289  */
290 static void WINAPI check_async_list(async_private *asp, DWORD status)
291 {
292     async_private *ovp;
293     DWORD ovp_status;
294
295     for( ovp = NtCurrentTeb()->pending_list; ovp && ovp != asp; ovp = ovp->next );
296
297     if(!ovp)
298             return;
299
300     if( status != STATUS_ALERTED )
301     {
302         ovp_status = status;
303         ovp->ops->set_status (ovp, status);
304     }
305     else ovp_status = ovp->ops->get_status (ovp);
306
307     if( ovp_status == STATUS_PENDING ) ovp->func( ovp );
308
309     /* This will destroy all but PENDING requests */
310     register_old_async( ovp );
311 }
312
313
314 /***********************************************************************
315  *              wait_reply
316  *
317  * Wait for a reply on the waiting pipe of the current thread.
318  */
319 static int wait_reply( void *cookie )
320 {
321     int signaled;
322     struct wake_up_reply reply;
323     for (;;)
324     {
325         int ret;
326         ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) );
327         if (ret == sizeof(reply))
328         {
329             if (!reply.cookie) break;  /* thread got killed */
330             if (reply.cookie == cookie) return reply.signaled;
331             /* we stole another reply, wait for the real one */
332             signaled = wait_reply( cookie );
333             /* and now put the wrong one back in the pipe */
334             for (;;)
335             {
336                 ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) );
337                 if (ret == sizeof(reply)) break;
338                 if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret );
339                 if (errno == EINTR) continue;
340                 server_protocol_perror("wakeup write");
341             }
342             return signaled;
343         }
344         if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret );
345         if (errno == EINTR) continue;
346         server_protocol_perror("wakeup read");
347     }
348     /* the server closed the connection; time to die... */
349     SYSDEPS_AbortThread(0);
350 }
351
352
353 /***********************************************************************
354  *              call_apcs
355  *
356  * Call outstanding APCs.
357  */
358 static void call_apcs( BOOL alertable )
359 {
360     FARPROC proc;
361     LARGE_INTEGER time;
362     void *arg1, *arg2, *arg3;
363
364     for (;;)
365     {
366         int type = APC_NONE;
367         SERVER_START_REQ( get_apc )
368         {
369             req->alertable = alertable;
370             if (!wine_server_call( req )) type = reply->type;
371             proc = reply->func;
372             arg1 = reply->arg1;
373             arg2 = reply->arg2;
374             arg3 = reply->arg3;
375         }
376         SERVER_END_REQ;
377
378         switch(type)
379         {
380         case APC_NONE:
381             return;  /* no more APCs */
382         case APC_ASYNC:
383             proc( arg1, arg2 );
384             break;
385         case APC_USER:
386             proc( arg1, arg2, arg3 );
387             break;
388         case APC_TIMER:
389             /* convert sec/usec to NT time */
390             RtlSecondsSince1970ToTime( (time_t)arg1, &time );
391             time.QuadPart += (DWORD)arg2 * 10;
392             proc( arg3, time.s.LowPart, time.s.HighPart );
393             break;
394         case APC_ASYNC_IO:
395             check_async_list( arg1, (DWORD) arg2 );
396             break;
397         default:
398             server_protocol_error( "get_apc_request: bad type %d\n", type );
399             break;
400         }
401     }
402 }
403
404 /* wait operations */
405
406 /******************************************************************
407  *              NtWaitForMultipleObjects (NTDLL.@)
408  */
409 NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles,
410                                           BOOLEAN wait_all, BOOLEAN alertable,
411                                           PLARGE_INTEGER timeout )
412 {
413     int ret, cookie;
414
415     if (count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1;
416
417     for (;;)
418     {
419         SERVER_START_REQ( select )
420         {
421             req->flags   = SELECT_INTERRUPTIBLE;
422             req->cookie  = &cookie;
423             NTDLL_get_server_timeout( &req->timeout, timeout );
424             wine_server_add_data( req, handles, count * sizeof(HANDLE) );
425
426             if (wait_all) req->flags |= SELECT_ALL;
427             if (alertable) req->flags |= SELECT_ALERTABLE;
428             if (timeout) req->flags |= SELECT_TIMEOUT;
429
430             ret = wine_server_call( req );
431         }
432         SERVER_END_REQ;
433         if (ret == STATUS_PENDING) ret = wait_reply( &cookie );
434         if (ret != STATUS_USER_APC) break;
435         call_apcs( alertable );
436         if (alertable) break;
437     }
438     return ret;
439 }
440
441
442 /******************************************************************
443  *              NtWaitForSingleObject (NTDLL.@)
444  */
445 NTSTATUS WINAPI NtWaitForSingleObject(HANDLE handle, BOOLEAN alertable, PLARGE_INTEGER timeout )
446 {
447     return NtWaitForMultipleObjects( 1, &handle, FALSE, alertable, timeout );
448 }