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