Don't open device if already open.
[wine] / dlls / ntdll / sync.c
1 /*
2  *      Process synchronisation
3  *
4  * Copyright 1996, 1997, 1998 Marcus Meissner
5  * Copyright 1997, 1999 Alexandre Julliard
6  * Copyright 1999, 2000 Juergen Schmied
7  * Copyright 2003 Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <signal.h>
29 #ifdef HAVE_SYS_TIME_H
30 # include <sys/time.h>
31 #endif
32 #ifdef HAVE_POLL_H
33 #include <poll.h>
34 #endif
35 #ifdef HAVE_SYS_POLL_H
36 # include <sys/poll.h>
37 #endif
38 #ifdef HAVE_UNISTD_H
39 # include <unistd.h>
40 #endif
41 #ifdef HAVE_SCHED_H
42 # include <sched.h>
43 #endif
44 #include <string.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <time.h>
49
50 #define NONAMELESSUNION
51 #define NONAMELESSSTRUCT
52
53 #include "windef.h"
54 #include "winbase.h"
55 #include "thread.h"
56 #include "wine/server.h"
57 #include "wine/debug.h"
58 #include "ntdll_misc.h"
59
60 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
61
62
63 /*
64  *      Semaphores
65  */
66
67 /******************************************************************************
68  *  NtCreateSemaphore (NTDLL.@)
69  */
70 NTSTATUS WINAPI NtCreateSemaphore( OUT PHANDLE SemaphoreHandle,
71                                    IN ACCESS_MASK access,
72                                    IN const OBJECT_ATTRIBUTES *attr OPTIONAL,
73                                    IN LONG InitialCount,
74                                    IN LONG MaximumCount )
75 {
76     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
77     NTSTATUS ret;
78
79     if (MaximumCount <= 0 || InitialCount < 0 || InitialCount > MaximumCount)
80         return STATUS_INVALID_PARAMETER;
81     if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
82
83     SERVER_START_REQ( create_semaphore )
84     {
85         req->access  = access;
86         req->initial = InitialCount;
87         req->max     = MaximumCount;
88         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
89         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
90         ret = wine_server_call( req );
91         *SemaphoreHandle = reply->handle;
92     }
93     SERVER_END_REQ;
94     return ret;
95 }
96
97 /******************************************************************************
98  *  NtOpenSemaphore (NTDLL.@)
99  */
100 NTSTATUS WINAPI NtOpenSemaphore( OUT PHANDLE SemaphoreHandle,
101                                  IN ACCESS_MASK access,
102                                  IN const OBJECT_ATTRIBUTES *attr )
103 {
104     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
105     NTSTATUS ret;
106
107     if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
108
109     SERVER_START_REQ( open_semaphore )
110     {
111         req->access  = access;
112         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
113         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
114         ret = wine_server_call( req );
115         *SemaphoreHandle = reply->handle;
116     }
117     SERVER_END_REQ;
118     return ret;
119 }
120
121 /******************************************************************************
122  *  NtQuerySemaphore (NTDLL.@)
123  */
124 NTSTATUS WINAPI NtQuerySemaphore(
125         HANDLE SemaphoreHandle,
126         PVOID SemaphoreInformationClass,
127         OUT PVOID SemaphoreInformation,
128         ULONG Length,
129         PULONG ReturnLength)
130 {
131         FIXME("(%p,%p,%p,0x%08lx,%p) stub!\n",
132         SemaphoreHandle, SemaphoreInformationClass, SemaphoreInformation, Length, ReturnLength);
133         return STATUS_SUCCESS;
134 }
135
136 /******************************************************************************
137  *  NtReleaseSemaphore (NTDLL.@)
138  */
139 NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous )
140 {
141     NTSTATUS ret;
142     SERVER_START_REQ( release_semaphore )
143     {
144         req->handle = handle;
145         req->count  = count;
146         if (!(ret = wine_server_call( req )))
147         {
148             if (previous) *previous = reply->prev_count;
149         }
150     }
151     SERVER_END_REQ;
152     return ret;
153 }
154
155 /*
156  *      Events
157  */
158
159 /**************************************************************************
160  * NtCreateEvent (NTDLL.@)
161  * ZwCreateEvent (NTDLL.@)
162  */
163 NTSTATUS WINAPI NtCreateEvent(
164         OUT PHANDLE EventHandle,
165         IN ACCESS_MASK DesiredAccess,
166         IN const OBJECT_ATTRIBUTES *attr,
167         IN BOOLEAN ManualReset,
168         IN BOOLEAN InitialState)
169 {
170     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
171     NTSTATUS ret;
172
173     if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
174
175     SERVER_START_REQ( create_event )
176     {
177         req->access = DesiredAccess;
178         req->manual_reset = ManualReset;
179         req->initial_state = InitialState;
180         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
181         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
182         ret = wine_server_call( req );
183         *EventHandle = reply->handle;
184     }
185     SERVER_END_REQ;
186     return ret;
187 }
188
189 /******************************************************************************
190  *  NtOpenEvent (NTDLL.@)
191  *  ZwOpenEvent (NTDLL.@)
192  */
193 NTSTATUS WINAPI NtOpenEvent(
194         OUT PHANDLE EventHandle,
195         IN ACCESS_MASK DesiredAccess,
196         IN const OBJECT_ATTRIBUTES *attr )
197 {
198     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
199     NTSTATUS ret;
200
201     if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
202
203     SERVER_START_REQ( open_event )
204     {
205         req->access  = DesiredAccess;
206         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
207         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
208         ret = wine_server_call( req );
209         *EventHandle = reply->handle;
210     }
211     SERVER_END_REQ;
212     return ret;
213 }
214
215
216 /******************************************************************************
217  *  NtSetEvent (NTDLL.@)
218  *  ZwSetEvent (NTDLL.@)
219  */
220 NTSTATUS WINAPI NtSetEvent( HANDLE handle, PULONG NumberOfThreadsReleased )
221 {
222     NTSTATUS ret;
223
224     /* FIXME: set NumberOfThreadsReleased */
225
226     SERVER_START_REQ( event_op )
227     {
228         req->handle = handle;
229         req->op     = SET_EVENT;
230         ret = wine_server_call( req );
231     }
232     SERVER_END_REQ;
233     return ret;
234 }
235
236 /******************************************************************************
237  *  NtResetEvent (NTDLL.@)
238  */
239 NTSTATUS WINAPI NtResetEvent( HANDLE handle, PULONG NumberOfThreadsReleased )
240 {
241     NTSTATUS ret;
242
243     /* resetting an event can't release any thread... */
244     if (NumberOfThreadsReleased) *NumberOfThreadsReleased = 0;
245
246     SERVER_START_REQ( event_op )
247     {
248         req->handle = handle;
249         req->op     = RESET_EVENT;
250         ret = wine_server_call( req );
251     }
252     SERVER_END_REQ;
253     return ret;
254 }
255
256 /******************************************************************************
257  *  NtClearEvent (NTDLL.@)
258  *
259  * FIXME
260  *   same as NtResetEvent ???
261  */
262 NTSTATUS WINAPI NtClearEvent ( HANDLE handle )
263 {
264     return NtResetEvent( handle, NULL );
265 }
266
267 /******************************************************************************
268  *  NtPulseEvent (NTDLL.@)
269  *
270  * FIXME
271  *   PulseCount
272  */
273 NTSTATUS WINAPI NtPulseEvent( HANDLE handle, PULONG PulseCount )
274 {
275     NTSTATUS ret;
276
277     if (PulseCount)
278       FIXME("(%p,%ld)\n", handle, *PulseCount);
279
280     SERVER_START_REQ( event_op )
281     {
282         req->handle = handle;
283         req->op     = PULSE_EVENT;
284         ret = wine_server_call( req );
285     }
286     SERVER_END_REQ;
287     return ret;
288 }
289
290 /******************************************************************************
291  *  NtQueryEvent (NTDLL.@)
292  */
293 NTSTATUS WINAPI NtQueryEvent (
294         IN  HANDLE EventHandle,
295         IN  UINT EventInformationClass,
296         OUT PVOID EventInformation,
297         IN  ULONG EventInformationLength,
298         OUT PULONG  ReturnLength)
299 {
300         FIXME("(%p)\n", EventHandle);
301         return STATUS_SUCCESS;
302 }
303
304 /*
305  *      Mutants (known as Mutexes in Kernel32)
306  */
307
308 /******************************************************************************
309  *              NtCreateMutant                          [NTDLL.@]
310  *              ZwCreateMutant                          [NTDLL.@]
311  */
312 NTSTATUS WINAPI NtCreateMutant(OUT HANDLE* MutantHandle,
313                                IN ACCESS_MASK access,
314                                IN const OBJECT_ATTRIBUTES* attr OPTIONAL,
315                                IN BOOLEAN InitialOwner)
316 {
317     NTSTATUS    status;
318     DWORD       len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
319
320     if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
321
322     SERVER_START_REQ( create_mutex )
323     {
324         req->access  = access;
325         req->owned   = InitialOwner;
326         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
327         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
328         status = wine_server_call( req );
329         *MutantHandle = reply->handle;
330     }
331     SERVER_END_REQ;
332     return status;
333 }
334
335 /**************************************************************************
336  *              NtOpenMutant                            [NTDLL.@]
337  *              ZwOpenMutant                            [NTDLL.@]
338  */
339 NTSTATUS WINAPI NtOpenMutant(OUT HANDLE* MutantHandle, 
340                              IN ACCESS_MASK access, 
341                              IN const OBJECT_ATTRIBUTES* attr )
342 {
343     NTSTATUS    status;
344     DWORD       len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
345
346     if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
347
348     SERVER_START_REQ( open_mutex )
349     {
350         req->access  = access;
351         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
352         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
353         status = wine_server_call( req );
354         *MutantHandle = reply->handle;
355     }
356     SERVER_END_REQ;
357     return status;
358 }
359
360 /**************************************************************************
361  *              NtReleaseMutant                         [NTDLL.@]
362  *              ZwReleaseMutant                         [NTDLL.@]
363  */
364 NTSTATUS WINAPI NtReleaseMutant( IN HANDLE handle, OUT PLONG prev_count OPTIONAL)
365 {
366     NTSTATUS    status;
367
368     SERVER_START_REQ( release_mutex )
369     {
370         req->handle = handle;
371         status = wine_server_call( req );
372         if (prev_count) *prev_count = reply->prev_count;
373     }
374     SERVER_END_REQ;
375     return status;
376 }
377
378 /******************************************************************
379  *              NtQueryMutant                   [NTDLL.@]
380  *              ZwQueryMutant                   [NTDLL.@]
381  */
382 NTSTATUS WINAPI NtQueryMutant(IN HANDLE handle, 
383                               IN MUTANT_INFORMATION_CLASS MutantInformationClass, 
384                               OUT PVOID MutantInformation, 
385                               IN ULONG MutantInformationLength, 
386                               OUT PULONG ResultLength OPTIONAL )
387 {
388     FIXME("(%p %u %p %lu %p): stub!\n", 
389           handle, MutantInformationClass, MutantInformation, MutantInformationLength, ResultLength);
390     return STATUS_NOT_IMPLEMENTED;
391 }
392
393 /*
394  *      Timers
395  */
396
397 /**************************************************************************
398  *              NtCreateTimer                           [NTDLL.@]
399  *              ZwCreateTimer                           [NTDLL.@]
400  */
401 NTSTATUS WINAPI NtCreateTimer(OUT HANDLE *handle,
402                               IN ACCESS_MASK access,
403                               IN const OBJECT_ATTRIBUTES *attr OPTIONAL,
404                               IN TIMER_TYPE timer_type)
405 {
406     DWORD       len = (attr && attr->ObjectName) ? attr->ObjectName->Length : 0;
407     NTSTATUS    status;
408
409     if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
410
411     if (timer_type != NotificationTimer && timer_type != SynchronizationTimer)
412         return STATUS_INVALID_PARAMETER;
413
414     SERVER_START_REQ( create_timer )
415     {
416         req->access  = access;
417         req->manual  = (timer_type == NotificationTimer) ? TRUE : FALSE;
418         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
419         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
420         status = wine_server_call( req );
421         *handle = reply->handle;
422     }
423     SERVER_END_REQ;
424     return status;
425
426 }
427
428 /**************************************************************************
429  *              NtOpenTimer                             [NTDLL.@]
430  *              ZwOpenTimer                             [NTDLL.@]
431  */
432 NTSTATUS WINAPI NtOpenTimer(OUT PHANDLE handle,
433                             IN ACCESS_MASK access,
434                             IN const OBJECT_ATTRIBUTES* attr )
435 {
436     DWORD       len = (attr && attr->ObjectName) ? attr->ObjectName->Length : 0;
437     NTSTATUS    status;
438
439     if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG;
440
441     SERVER_START_REQ( open_timer )
442     {
443         req->access  = access;
444         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
445         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
446         status = wine_server_call( req );
447         *handle = reply->handle;
448     }
449     SERVER_END_REQ;
450     return status;
451 }
452
453 /**************************************************************************
454  *              NtSetTimer                              [NTDLL.@]
455  *              ZwSetTimer                              [NTDLL.@]
456  */
457 NTSTATUS WINAPI NtSetTimer(IN HANDLE handle,
458                            IN const LARGE_INTEGER* when,
459                            IN PTIMERAPCROUTINE callback,
460                            IN PVOID callback_arg,
461                            IN BOOLEAN resume,
462                            IN ULONG period OPTIONAL,
463                            OUT PBOOLEAN state OPTIONAL)
464 {
465     NTSTATUS    status = STATUS_SUCCESS;
466
467     TRACE("(%p,%p,%p,%p,%08x,0x%08lx,%p) stub\n",
468           handle, when, callback, callback_arg, resume, period, state);
469
470     SERVER_START_REQ( set_timer )
471     {
472         if (!when->u.LowPart && !when->u.HighPart)
473         {
474             /* special case to start timeout on now+period without too many calculations */
475             req->expire.sec  = 0;
476             req->expire.usec = 0;
477         }
478         else NTDLL_get_server_timeout( &req->expire, when );
479
480         req->handle   = handle;
481         req->period   = period;
482         req->callback = callback;
483         req->arg      = callback_arg;
484         status = wine_server_call( req );
485         if (state) *state = reply->signaled;
486     }
487     SERVER_END_REQ;
488
489     /* set error but can still succeed */
490     if (resume && status == STATUS_SUCCESS) return STATUS_TIMER_RESUME_IGNORED;
491     return status;
492 }
493
494 /**************************************************************************
495  *              NtCancelTimer                           [NTDLL.@]
496  *              ZwCancelTimer                           [NTDLL.@]
497  */
498 NTSTATUS WINAPI NtCancelTimer(IN HANDLE handle, OUT BOOLEAN* state)
499 {
500     NTSTATUS    status;
501
502     SERVER_START_REQ( cancel_timer )
503     {
504         req->handle = handle;
505         status = wine_server_call( req );
506         if (state) *state = reply->signaled;
507     }
508     SERVER_END_REQ;
509     return status;
510 }
511
512 /******************************************************************************
513  *  NtQueryTimer (NTDLL.@)
514  *
515  * Retrieves information about a timer.
516  *
517  * PARAMS
518  *  TimerHandle           [I] The timer to retrieve information about.
519  *  TimerInformationClass [I] The type of information to retrieve.
520  *  TimerInformation      [O] Pointer to buffer to store information in.
521  *  Length                [I] The length of the buffer pointed to by TimerInformation.
522  *  ReturnLength          [O] Optional. The size of buffer actually used.
523  *
524  * RETURNS
525  *  Success: STATUS_SUCCESS
526  *  Failure: STATUS_INFO_LENGTH_MISMATCH, if Length doesn't match the required data
527  *           size for the class specified.
528  *           STATUS_INVALID_INFO_CLASS, if an invalid TimerInformationClass was specified.
529  *           STATUS_ACCESS_DENIED, if TimerHandle does not have TIMER_QUERY_STATE access
530  *           to the timer.
531  */
532 NTSTATUS WINAPI NtQueryTimer(
533     HANDLE TimerHandle,
534     TIMER_INFORMATION_CLASS TimerInformationClass,
535     PVOID TimerInformation,
536     ULONG Length,
537     PULONG ReturnLength)
538 {
539     TIMER_BASIC_INFORMATION * basic_info = (TIMER_BASIC_INFORMATION *)TimerInformation;
540     NTSTATUS status;
541     LARGE_INTEGER now;
542
543     TRACE("(%p,%d,%p,0x%08lx,%p)\n", TimerHandle, TimerInformationClass,
544        TimerInformation, Length, ReturnLength);
545
546     switch (TimerInformationClass)
547     {
548     case TimerBasicInformation:
549         if (Length < sizeof(TIMER_BASIC_INFORMATION))
550             return STATUS_INFO_LENGTH_MISMATCH;
551
552         SERVER_START_REQ(get_timer_info)
553         {
554             req->handle = TimerHandle;
555             status = wine_server_call(req);
556
557             /* convert server time to absolute NTDLL time */
558             NTDLL_from_server_timeout(&basic_info->RemainingTime, &reply->when);
559             basic_info->TimerState = reply->signaled;
560         }
561         SERVER_END_REQ;
562
563         /* convert from absolute into relative time */
564         NtQuerySystemTime(&now);
565         if (now.QuadPart > basic_info->RemainingTime.QuadPart)
566             basic_info->RemainingTime.QuadPart = 0;
567         else
568             basic_info->RemainingTime.QuadPart -= now.QuadPart;
569
570         if (ReturnLength) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
571
572         return status;
573     }
574
575     FIXME("Unhandled class %d\n", TimerInformationClass);
576     return STATUS_INVALID_INFO_CLASS;
577 }
578
579
580 /******************************************************************************
581  * NtQueryTimerResolution [NTDLL.@]
582  */
583 NTSTATUS WINAPI NtQueryTimerResolution(OUT ULONG* min_resolution,
584                                        OUT ULONG* max_resolution,
585                                        OUT ULONG* current_resolution)
586 {
587     FIXME("(%p,%p,%p), stub!\n",
588           min_resolution, max_resolution, current_resolution);
589
590     return STATUS_NOT_IMPLEMENTED;
591 }
592
593 /******************************************************************************
594  * NtSetTimerResolution [NTDLL.@]
595  */
596 NTSTATUS WINAPI NtSetTimerResolution(IN ULONG resolution,
597                                      IN BOOLEAN set_resolution,
598                                      OUT ULONG* current_resolution )
599 {
600     FIXME("(%lu,%u,%p), stub!\n",
601           resolution, set_resolution, current_resolution);
602
603     return STATUS_NOT_IMPLEMENTED;
604 }
605
606
607 /***********************************************************************
608  *              wait_reply
609  *
610  * Wait for a reply on the waiting pipe of the current thread.
611  */
612 static int wait_reply( void *cookie )
613 {
614     int signaled;
615     struct wake_up_reply reply;
616     for (;;)
617     {
618         int ret;
619         ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) );
620         if (ret == sizeof(reply))
621         {
622             if (!reply.cookie) break;  /* thread got killed */
623             if (reply.cookie == cookie) return reply.signaled;
624             /* we stole another reply, wait for the real one */
625             signaled = wait_reply( cookie );
626             /* and now put the wrong one back in the pipe */
627             for (;;)
628             {
629                 ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) );
630                 if (ret == sizeof(reply)) break;
631                 if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret );
632                 if (errno == EINTR) continue;
633                 server_protocol_perror("wakeup write");
634             }
635             return signaled;
636         }
637         if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret );
638         if (errno == EINTR) continue;
639         server_protocol_perror("wakeup read");
640     }
641     /* the server closed the connection; time to die... */
642     server_abort_thread(0);
643 }
644
645
646 /***********************************************************************
647  *              call_apcs
648  *
649  * Call outstanding APCs.
650  */
651 static void call_apcs( BOOL alertable )
652 {
653     FARPROC proc;
654     LARGE_INTEGER time;
655     void *arg1, *arg2, *arg3;
656
657     for (;;)
658     {
659         int type = APC_NONE;
660         SERVER_START_REQ( get_apc )
661         {
662             req->alertable = alertable;
663             if (!wine_server_call( req )) type = reply->type;
664             proc = reply->func;
665             arg1 = reply->arg1;
666             arg2 = reply->arg2;
667             arg3 = reply->arg3;
668         }
669         SERVER_END_REQ;
670
671         switch (type)
672         {
673         case APC_NONE:
674             return;  /* no more APCs */
675         case APC_USER:
676             proc( arg1, arg2, arg3 );
677             break;
678         case APC_TIMER:
679             /* convert sec/usec to NT time */
680             RtlSecondsSince1970ToTime( (time_t)arg1, &time );
681             time.QuadPart += (DWORD)arg2 * 10;
682             proc( arg3, time.u.LowPart, time.u.HighPart );
683             break;
684         case APC_ASYNC_IO:
685             NtCurrentTeb()->num_async_io--;
686             proc( arg1, (IO_STATUS_BLOCK*)arg2, (ULONG)arg3 );
687             break;
688         default:
689             server_protocol_error( "get_apc_request: bad type %d\n", type );
690             break;
691         }
692     }
693 }
694
695
696 /***********************************************************************
697  *              NTDLL_wait_for_multiple_objects
698  *
699  * Implementation of NtWaitForMultipleObjects
700  */
701 NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UINT flags,
702                                           const LARGE_INTEGER *timeout, HANDLE signal_object )
703 {
704     NTSTATUS ret;
705     int cookie;
706
707     if (timeout) flags |= SELECT_TIMEOUT;
708     for (;;)
709     {
710         SERVER_START_REQ( select )
711         {
712             req->flags   = flags;
713             req->cookie  = &cookie;
714             req->signal  = signal_object;
715             NTDLL_get_server_timeout( &req->timeout, timeout );
716             wine_server_add_data( req, handles, count * sizeof(HANDLE) );
717             ret = wine_server_call( req );
718         }
719         SERVER_END_REQ;
720         if (ret == STATUS_PENDING) ret = wait_reply( &cookie );
721         if (ret != STATUS_USER_APC) break;
722         call_apcs( (flags & SELECT_ALERTABLE) != 0 );
723         if (flags & SELECT_ALERTABLE) break;
724         signal_object = 0;  /* don't signal it multiple times */
725     }
726
727     /* A test on Windows 2000 shows that Windows always yields during
728        a wait, but a wait that is hit by an event gets a priority
729        boost as well.  This seems to model that behavior the closest.  */
730     if (ret == WAIT_TIMEOUT) NtYieldExecution();
731
732     return ret;
733 }
734
735
736 /* wait operations */
737
738 /******************************************************************
739  *              NtWaitForMultipleObjects (NTDLL.@)
740  */
741 NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles,
742                                           BOOLEAN wait_all, BOOLEAN alertable,
743                                           const LARGE_INTEGER *timeout )
744 {
745     UINT flags = SELECT_INTERRUPTIBLE;
746
747     if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1;
748
749     if (wait_all) flags |= SELECT_ALL;
750     if (alertable) flags |= SELECT_ALERTABLE;
751     return NTDLL_wait_for_multiple_objects( count, handles, flags, timeout, 0 );
752 }
753
754
755 /******************************************************************
756  *              NtWaitForSingleObject (NTDLL.@)
757  */
758 NTSTATUS WINAPI NtWaitForSingleObject(HANDLE handle, BOOLEAN alertable, const LARGE_INTEGER *timeout )
759 {
760     return NtWaitForMultipleObjects( 1, &handle, FALSE, alertable, timeout );
761 }
762
763
764 /******************************************************************
765  *              NtSignalAndWaitForSingleObject (NTDLL.@)
766  */
767 NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE hSignalObject, HANDLE hWaitObject,
768                                                 BOOLEAN alertable, const LARGE_INTEGER *timeout )
769 {
770     UINT flags = SELECT_INTERRUPTIBLE;
771
772     if (!hSignalObject) return STATUS_INVALID_HANDLE;
773     if (alertable) flags |= SELECT_ALERTABLE;
774     return NTDLL_wait_for_multiple_objects( 1, &hWaitObject, flags, timeout, hSignalObject );
775 }
776
777
778 /******************************************************************
779  *              NtYieldExecution (NTDLL.@)
780  */
781 NTSTATUS WINAPI NtYieldExecution(void)
782 {
783 #ifdef HAVE_SCHED_YIELD
784     sched_yield();
785     return STATUS_SUCCESS;
786 #else
787     return STATUS_NO_YIELD_PERFORMED;
788 #endif
789 }
790
791
792 /******************************************************************
793  *              NtDelayExecution (NTDLL.@)
794  */
795 NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout )
796 {
797     /* if alertable or async I/O in progress, we need to query the server */
798     if (alertable || NtCurrentTeb()->num_async_io)
799     {
800         UINT flags = SELECT_INTERRUPTIBLE;
801         if (alertable) flags |= SELECT_ALERTABLE;
802         return NTDLL_wait_for_multiple_objects( 0, NULL, flags, timeout, 0 );
803     }
804
805     if (!timeout)  /* sleep forever */
806     {
807         for (;;) select( 0, NULL, NULL, NULL, NULL );
808     }
809     else
810     {
811         abs_time_t when;
812
813         NTDLL_get_server_timeout( &when, timeout );
814
815         /* Note that we yield after establishing the desired timeout */
816         NtYieldExecution();
817
818         for (;;)
819         {
820             struct timeval tv;
821             gettimeofday( &tv, 0 );
822             tv.tv_sec = when.sec - tv.tv_sec;
823             if ((tv.tv_usec = when.usec - tv.tv_usec) < 0)
824             {
825                 tv.tv_usec += 1000000;
826                 tv.tv_sec--;
827             }
828             /* if our yield already passed enough time, we're done */
829             if (tv.tv_sec < 0) break;
830
831             if (select( 0, NULL, NULL, NULL, &tv ) != -1) break;
832         }
833     }
834     return STATUS_SUCCESS;
835 }