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