Implemented NtYieldExecution.
[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_SYS_POLL_H
33 # include <sys/poll.h>
34 #endif
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #ifdef HAVE_SCHED_H
39 # include <sched.h>
40 #endif
41 #include <string.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <time.h>
46
47 #define NONAMELESSUNION
48 #define NONAMELESSSTRUCT
49
50 #include "windef.h"
51 #include "winbase.h"
52 #include "winreg.h"
53 #include "winternl.h"
54 #include "async.h"
55 #include "thread.h"
56 #include "wine/server.h"
57 #include "wine/unicode.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 ULONG InitialCount,
75                                    IN ULONG MaximumCount )
76 {
77     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
78     NTSTATUS ret;
79
80     if ((MaximumCount <= 0) || (InitialCount > MaximumCount))
81         return STATUS_INVALID_PARAMETER;
82
83     SERVER_START_REQ( create_semaphore )
84     {
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     SERVER_START_REQ( open_semaphore )
107     {
108         req->access  = access;
109         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
110         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
111         ret = wine_server_call( req );
112         *SemaphoreHandle = reply->handle;
113     }
114     SERVER_END_REQ;
115     return ret;
116 }
117
118 /******************************************************************************
119  *  NtQuerySemaphore (NTDLL.@)
120  */
121 NTSTATUS WINAPI NtQuerySemaphore(
122         HANDLE SemaphoreHandle,
123         PVOID SemaphoreInformationClass,
124         OUT PVOID SemaphoreInformation,
125         ULONG Length,
126         PULONG ReturnLength)
127 {
128         FIXME("(%p,%p,%p,0x%08lx,%p) stub!\n",
129         SemaphoreHandle, SemaphoreInformationClass, SemaphoreInformation, Length, ReturnLength);
130         return STATUS_SUCCESS;
131 }
132
133 /******************************************************************************
134  *  NtReleaseSemaphore (NTDLL.@)
135  */
136 NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous )
137 {
138     NTSTATUS ret;
139     SERVER_START_REQ( release_semaphore )
140     {
141         req->handle = handle;
142         req->count  = count;
143         if (!(ret = wine_server_call( req )))
144         {
145             if (previous) *previous = reply->prev_count;
146         }
147     }
148     SERVER_END_REQ;
149     return ret;
150 }
151
152 /*
153  *      Events
154  */
155
156 /**************************************************************************
157  * NtCreateEvent (NTDLL.@)
158  * ZwCreateEvent (NTDLL.@)
159  */
160 NTSTATUS WINAPI NtCreateEvent(
161         OUT PHANDLE EventHandle,
162         IN ACCESS_MASK DesiredAccess,
163         IN const OBJECT_ATTRIBUTES *attr,
164         IN BOOLEAN ManualReset,
165         IN BOOLEAN InitialState)
166 {
167     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
168     NTSTATUS ret;
169
170     SERVER_START_REQ( create_event )
171     {
172         req->manual_reset = ManualReset;
173         req->initial_state = InitialState;
174         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
175         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
176         ret = wine_server_call( req );
177         *EventHandle = reply->handle;
178     }
179     SERVER_END_REQ;
180     return ret;
181 }
182
183 /******************************************************************************
184  *  NtOpenEvent (NTDLL.@)
185  *  ZwOpenEvent (NTDLL.@)
186  */
187 NTSTATUS WINAPI NtOpenEvent(
188         OUT PHANDLE EventHandle,
189         IN ACCESS_MASK DesiredAccess,
190         IN const OBJECT_ATTRIBUTES *attr )
191 {
192     DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0;
193     NTSTATUS ret;
194
195     SERVER_START_REQ( open_event )
196     {
197         req->access  = DesiredAccess;
198         req->inherit = attr && (attr->Attributes & OBJ_INHERIT);
199         if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len );
200         ret = wine_server_call( req );
201         *EventHandle = reply->handle;
202     }
203     SERVER_END_REQ;
204     return ret;
205 }
206
207
208 /******************************************************************************
209  *  NtSetEvent (NTDLL.@)
210  *  ZwSetEvent (NTDLL.@)
211  */
212 NTSTATUS WINAPI NtSetEvent( HANDLE handle, PULONG NumberOfThreadsReleased )
213 {
214     NTSTATUS ret;
215
216     /* FIXME: set NumberOfThreadsReleased */
217
218     SERVER_START_REQ( event_op )
219     {
220         req->handle = handle;
221         req->op     = SET_EVENT;
222         ret = wine_server_call( req );
223     }
224     SERVER_END_REQ;
225     return ret;
226 }
227
228 /******************************************************************************
229  *  NtResetEvent (NTDLL.@)
230  */
231 NTSTATUS WINAPI NtResetEvent( HANDLE handle, PULONG NumberOfThreadsReleased )
232 {
233     NTSTATUS ret;
234
235     /* resetting an event can't release any thread... */
236     if (NumberOfThreadsReleased) *NumberOfThreadsReleased = 0;
237
238     SERVER_START_REQ( event_op )
239     {
240         req->handle = handle;
241         req->op     = RESET_EVENT;
242         ret = wine_server_call( req );
243     }
244     SERVER_END_REQ;
245     return ret;
246 }
247
248 /******************************************************************************
249  *  NtClearEvent (NTDLL.@)
250  *
251  * FIXME
252  *   same as NtResetEvent ???
253  */
254 NTSTATUS WINAPI NtClearEvent ( HANDLE handle )
255 {
256     return NtResetEvent( handle, NULL );
257 }
258
259 /******************************************************************************
260  *  NtPulseEvent (NTDLL.@)
261  *
262  * FIXME
263  *   PulseCount
264  */
265 NTSTATUS WINAPI NtPulseEvent( HANDLE handle, PULONG PulseCount )
266 {
267     NTSTATUS ret;
268
269     if (PulseCount)
270       FIXME("(%p,%ld)\n", handle, *PulseCount);
271
272     SERVER_START_REQ( event_op )
273     {
274         req->handle = handle;
275         req->op     = PULSE_EVENT;
276         ret = wine_server_call( req );
277     }
278     SERVER_END_REQ;
279     return ret;
280 }
281
282 /******************************************************************************
283  *  NtQueryEvent (NTDLL.@)
284  */
285 NTSTATUS WINAPI NtQueryEvent (
286         IN  HANDLE EventHandle,
287         IN  UINT EventInformationClass,
288         OUT PVOID EventInformation,
289         IN  ULONG EventInformationLength,
290         OUT PULONG  ReturnLength)
291 {
292         FIXME("(%p)\n", EventHandle);
293         return STATUS_SUCCESS;
294 }
295
296
297 /*
298  *      Timers
299  */
300
301 /**************************************************************************
302  *              NtCreateTimer                           [NTDLL.@]
303  *              ZwCreateTimer                           [NTDLL.@]
304  */
305 NTSTATUS WINAPI NtCreateTimer(OUT HANDLE *handle,
306                               IN ACCESS_MASK access,
307                               IN const OBJECT_ATTRIBUTES *oa OPTIONAL,
308                               IN TIMER_TYPE timer_type)
309 {
310     DWORD len = (oa && oa->ObjectName) ? oa->ObjectName->Length : 0;
311     NTSTATUS    status;
312
313     if (timer_type != NotificationTimer && timer_type != SynchronizationTimer)
314         return STATUS_INVALID_PARAMETER;
315
316     SERVER_START_REQ( create_timer )
317     {
318         req->manual  = (timer_type == NotificationTimer) ? TRUE : FALSE;
319         req->inherit = oa && (oa->Attributes & OBJ_INHERIT);
320         if (len) wine_server_add_data( req, oa->ObjectName->Buffer, len );
321         status = wine_server_call( req );
322         *handle = reply->handle;
323     }
324     SERVER_END_REQ;
325     return status;
326
327 }
328
329 /**************************************************************************
330  *              NtOpenTimer                             [NTDLL.@]
331  *              ZwOpenTimer                             [NTDLL.@]
332  */
333 NTSTATUS WINAPI NtOpenTimer(OUT PHANDLE handle,
334                             IN ACCESS_MASK access,
335                             IN const OBJECT_ATTRIBUTES* oa )
336 {
337     DWORD len = (oa && oa->ObjectName) ? oa->ObjectName->Length : 0;
338     NTSTATUS status;
339
340     if (oa && oa->Length >= MAX_PATH * sizeof(WCHAR))
341         return STATUS_NAME_TOO_LONG;
342
343     SERVER_START_REQ( open_timer )
344     {
345         req->access  = access;
346         req->inherit = oa && (oa->Attributes & OBJ_INHERIT);
347         if (len) wine_server_add_data( req, oa->ObjectName->Buffer, len );
348         status = wine_server_call( req );
349         *handle = reply->handle;
350     }
351     SERVER_END_REQ;
352     return status;
353 }
354
355 /**************************************************************************
356  *              NtSetTimer                              [NTDLL.@]
357  *              ZwSetTimer                              [NTDLL.@]
358  */
359 NTSTATUS WINAPI NtSetTimer(IN HANDLE handle,
360                            IN const LARGE_INTEGER* when,
361                            IN PTIMERAPCROUTINE callback,
362                            IN PVOID callback_arg,
363                            IN BOOLEAN resume,
364                            IN ULONG period OPTIONAL,
365                            OUT PBOOLEAN state OPTIONAL)
366 {
367     NTSTATUS    status = STATUS_SUCCESS;
368
369     TRACE("(%p,%p,%p,%p,%08x,0x%08lx,%p) stub\n",
370           handle, when, callback, callback_arg, resume, period, state);
371
372     SERVER_START_REQ( set_timer )
373     {
374         if (!when->u.LowPart && !when->u.HighPart)
375         {
376             /* special case to start timeout on now+period without too many calculations */
377             req->expire.sec  = 0;
378             req->expire.usec = 0;
379         }
380         else NTDLL_get_server_timeout( &req->expire, when );
381
382         req->handle   = handle;
383         req->period   = period;
384         req->callback = callback;
385         req->arg      = callback_arg;
386         status = wine_server_call( req );
387         if (state) *state = reply->signaled;
388     }
389     SERVER_END_REQ;
390
391     /* set error but can still succeed */
392     if (resume && status == STATUS_SUCCESS) return STATUS_TIMER_RESUME_IGNORED;
393     return status;
394 }
395
396 /**************************************************************************
397  *              NtCancelTimer                           [NTDLL.@]
398  *              ZwCancelTimer                           [NTDLL.@]
399  */
400 NTSTATUS WINAPI NtCancelTimer(IN HANDLE handle, OUT BOOLEAN* state)
401 {
402     NTSTATUS    status;
403
404     SERVER_START_REQ( cancel_timer )
405     {
406         req->handle = handle;
407         status = wine_server_call( req );
408         if (state) *state = reply->signaled;
409     }
410     SERVER_END_REQ;
411     return status;
412 }
413
414 /******************************************************************************
415  * NtQueryTimerResolution [NTDLL.@]
416  */
417 NTSTATUS WINAPI NtQueryTimerResolution(OUT ULONG* min_resolution,
418                                        OUT ULONG* max_resolution,
419                                        OUT ULONG* current_resolution)
420 {
421     FIXME("(%p,%p,%p), stub!\n",
422           min_resolution, max_resolution, current_resolution);
423
424     return STATUS_NOT_IMPLEMENTED;
425 }
426
427 /******************************************************************************
428  * NtSetTimerResolution [NTDLL.@]
429  */
430 NTSTATUS WINAPI NtSetTimerResolution(IN ULONG resolution,
431                                      IN BOOLEAN set_resolution,
432                                      OUT ULONG* current_resolution )
433 {
434     FIXME("(%lu,%u,%p), stub!\n",
435           resolution, set_resolution, current_resolution);
436
437     return STATUS_NOT_IMPLEMENTED;
438 }
439
440
441 /***********************************************************************
442  *           check_async_list
443  *
444  * Process a status event from the server.
445  */
446 static void WINAPI check_async_list(async_private *asp, DWORD status)
447 {
448     async_private *ovp;
449     DWORD ovp_status;
450
451     for( ovp = NtCurrentTeb()->pending_list; ovp && ovp != asp; ovp = ovp->next );
452
453     if(!ovp)
454             return;
455
456     if( status != STATUS_ALERTED )
457     {
458         ovp_status = status;
459         ovp->iosb->u.Status = status;
460     }
461     else ovp_status = ovp->iosb->u.Status;
462
463     if( ovp_status == STATUS_PENDING ) ovp->func( ovp );
464
465     /* This will destroy all but PENDING requests */
466     register_old_async( ovp );
467 }
468
469
470 /***********************************************************************
471  *              wait_reply
472  *
473  * Wait for a reply on the waiting pipe of the current thread.
474  */
475 static int wait_reply( void *cookie )
476 {
477     int signaled;
478     struct wake_up_reply reply;
479     for (;;)
480     {
481         int ret;
482         ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) );
483         if (ret == sizeof(reply))
484         {
485             if (!reply.cookie) break;  /* thread got killed */
486             if (reply.cookie == cookie) return reply.signaled;
487             /* we stole another reply, wait for the real one */
488             signaled = wait_reply( cookie );
489             /* and now put the wrong one back in the pipe */
490             for (;;)
491             {
492                 ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) );
493                 if (ret == sizeof(reply)) break;
494                 if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret );
495                 if (errno == EINTR) continue;
496                 server_protocol_perror("wakeup write");
497             }
498             return signaled;
499         }
500         if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret );
501         if (errno == EINTR) continue;
502         server_protocol_perror("wakeup read");
503     }
504     /* the server closed the connection; time to die... */
505     server_abort_thread(0);
506 }
507
508
509 /***********************************************************************
510  *              call_apcs
511  *
512  * Call outstanding APCs.
513  */
514 static void call_apcs( BOOL alertable )
515 {
516     FARPROC proc;
517     LARGE_INTEGER time;
518     void *arg1, *arg2, *arg3;
519
520     for (;;)
521     {
522         int type = APC_NONE;
523         SERVER_START_REQ( get_apc )
524         {
525             req->alertable = alertable;
526             if (!wine_server_call( req )) type = reply->type;
527             proc = reply->func;
528             arg1 = reply->arg1;
529             arg2 = reply->arg2;
530             arg3 = reply->arg3;
531         }
532         SERVER_END_REQ;
533
534         switch(type)
535         {
536         case APC_NONE:
537             return;  /* no more APCs */
538         case APC_ASYNC:
539             proc( arg1, arg2 );
540             break;
541         case APC_USER:
542             proc( arg1, arg2, arg3 );
543             break;
544         case APC_TIMER:
545             /* convert sec/usec to NT time */
546             RtlSecondsSince1970ToTime( (time_t)arg1, &time );
547             time.QuadPart += (DWORD)arg2 * 10;
548             proc( arg3, time.u.LowPart, time.u.HighPart );
549             break;
550         case APC_ASYNC_IO:
551             check_async_list( arg1, (DWORD) arg2 );
552             break;
553         default:
554             server_protocol_error( "get_apc_request: bad type %d\n", type );
555             break;
556         }
557     }
558 }
559
560
561 /***********************************************************************
562  *              NTDLL_wait_for_multiple_objects
563  *
564  * Implementation of NtWaitForMultipleObjects
565  */
566 NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UINT flags,
567                                           const LARGE_INTEGER *timeout )
568 {
569     NTSTATUS ret;
570     int cookie;
571
572     if (timeout) flags |= SELECT_TIMEOUT;
573     for (;;)
574     {
575         SERVER_START_REQ( select )
576         {
577             req->flags   = flags;
578             req->cookie  = &cookie;
579             NTDLL_get_server_timeout( &req->timeout, timeout );
580             wine_server_add_data( req, handles, count * sizeof(HANDLE) );
581             ret = wine_server_call( req );
582         }
583         SERVER_END_REQ;
584         if (ret == STATUS_PENDING) ret = wait_reply( &cookie );
585         if (ret != STATUS_USER_APC) break;
586         call_apcs( (flags & SELECT_ALERTABLE) != 0 );
587         if (flags & SELECT_ALERTABLE) break;
588     }
589     return ret;
590 }
591
592
593 /* wait operations */
594
595 /******************************************************************
596  *              NtWaitForMultipleObjects (NTDLL.@)
597  */
598 NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles,
599                                           BOOLEAN wait_all, BOOLEAN alertable,
600                                           const LARGE_INTEGER *timeout )
601 {
602     UINT flags = SELECT_INTERRUPTIBLE;
603
604     if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1;
605
606     if (wait_all) flags |= SELECT_ALL;
607     if (alertable) flags |= SELECT_ALERTABLE;
608     return NTDLL_wait_for_multiple_objects( count, handles, flags, timeout );
609 }
610
611
612 /******************************************************************
613  *              NtWaitForSingleObject (NTDLL.@)
614  */
615 NTSTATUS WINAPI NtWaitForSingleObject(HANDLE handle, BOOLEAN alertable, const LARGE_INTEGER *timeout )
616 {
617     return NtWaitForMultipleObjects( 1, &handle, FALSE, alertable, timeout );
618 }
619
620
621 /******************************************************************
622  *              NtYieldExecution (NTDLL.@)
623  */
624 NTSTATUS WINAPI NtYieldExecution(void)
625 {
626 #ifdef HAVE_SCHED_YIELD
627     sched_yield();
628     return STATUS_SUCCESS;
629 #else
630     return STATUS_NO_YIELD_PERFORMED;
631 #endif
632 }
633
634
635 /******************************************************************
636  *              NtDelayExecution (NTDLL.@)
637  */
638 NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout )
639 {
640     /* if alertable or async I/O in progress, we need to query the server */
641     if (alertable || NtCurrentTeb()->pending_list)
642     {
643         UINT flags = SELECT_INTERRUPTIBLE;
644         if (alertable) flags |= SELECT_ALERTABLE;
645         return NTDLL_wait_for_multiple_objects( 0, NULL, flags, timeout );
646     }
647
648     if (!timeout)  /* sleep forever */
649     {
650         for (;;) select( 0, NULL, NULL, NULL, NULL );
651     }
652     else if (!timeout->QuadPart)
653     {
654         NtYieldExecution();
655     }
656     else
657     {
658         abs_time_t when;
659
660         NTDLL_get_server_timeout( &when, timeout );
661         for (;;)
662         {
663             struct timeval tv;
664             gettimeofday( &tv, 0 );
665             tv.tv_sec = when.sec - tv.tv_sec;
666             if ((tv.tv_usec = when.usec - tv.tv_usec) < 0)
667             {
668                 tv.tv_usec += 1000000;
669                 tv.tv_sec--;
670             }
671             if (tv.tv_sec < 0) tv.tv_sec = tv.tv_usec = 0;
672             if (select( 0, NULL, NULL, NULL, &tv ) != -1) break;
673         }
674     }
675     return STATUS_SUCCESS;
676 }