From fbef57c0cef24f357c5f60fbfef37f0d347c0006 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 31 Mar 2003 01:37:04 +0000 Subject: [PATCH] Moved WaitForMultipleObjects to ntdll (based on a patch by Eric Pouech). Added NTDLL_get_server_timeout function to compute ntdll-style timeouts and adapted the timer code to use it. --- dlls/ntdll/ntdll.spec | 4 +- dlls/ntdll/ntdll_misc.h | 2 + dlls/ntdll/om.c | 13 --- dlls/ntdll/sync.c | 197 +++++++++++++++++++++++++++++++++++++- dlls/ntdll/time.c | 48 ++++++++-- include/winternl.h | 1 + scheduler/synchro.c | 207 ++++------------------------------------ scheduler/timer.c | 31 ++---- 8 files changed, 265 insertions(+), 238 deletions(-) diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index f97cb719b25..8fc7164113a 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -249,7 +249,7 @@ @ stdcall NtUnmapViewOfSection(long ptr) @ stub NtVdmControl @ stub NtW32Call -@ stub NtWaitForMultipleObjects +@ stdcall NtWaitForMultipleObjects(long ptr long long ptr) @ stub NtWaitForProcessMutant @ stdcall NtWaitForSingleObject(long long long) @ stub NtWaitHighEventPair @@ -773,7 +773,7 @@ @ stdcall ZwUnmapViewOfSection(long ptr) NtUnmapViewOfSection @ stub ZwVdmControl @ stub ZwW32Call -@ stub ZwWaitForMultipleObjects +@ stdcall ZwWaitForMultipleObjects(long ptr long long ptr) NtWaitForMultipleObjects @ stub ZwWaitForProcessMutant @ stdcall ZwWaitForSingleObject(long long long) NtWaitForSingleObject @ stub ZwWaitHighEventPair diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 7ac08c98a12..0809bee62ff 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -28,6 +28,8 @@ extern LPCSTR debugstr_us( const UNICODE_STRING *str ); extern void dump_ObjectAttributes (const OBJECT_ATTRIBUTES *ObjectAttributes); +extern void NTDLL_get_server_timeout( struct timeval *when, const LARGE_INTEGER *timeout ); + /* module handling */ extern FARPROC MODULE_GetProcAddress( HMODULE hModule, LPCSTR function, int hint, BOOL snoop ); extern WINE_MODREF *MODULE_AllocModRef( HMODULE hModule, LPCSTR filename ); diff --git a/dlls/ntdll/om.c b/dlls/ntdll/om.c index bbd371d2aab..c998b6fc0c0 100644 --- a/dlls/ntdll/om.c +++ b/dlls/ntdll/om.c @@ -268,19 +268,6 @@ NTSTATUS WINAPI NtClose( HANDLE Handle ) return ret; } -/****************************************************************************** - * NtWaitForSingleObject [NTDLL.@] - * ZwWaitForSingleObject [NTDLL.@] - */ -NTSTATUS WINAPI NtWaitForSingleObject( - IN HANDLE Object, - IN BOOLEAN Alertable, - IN PLARGE_INTEGER Time) -{ - FIXME("(%p,0x%08x,%p),stub!\n",Object,Alertable,Time); - return 0; -} - /* * Directory functions */ diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index a25f56cf0ef..40b5529ff0b 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -1,6 +1,7 @@ /* * Process synchronisation * + * Copyright 1997 Alexandre Julliard * Copyright 1999, 2000 Juergen Schmied * * This library is free software; you can redistribute it and/or @@ -18,16 +19,31 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" + +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_SYS_POLL_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#include #include #include -#include #include -#include "wine/debug.h" -#include "winerror.h" -#include "wine/unicode.h" -#include "wine/server.h" #include "winternl.h" +#include "async.h" +#include "thread.h" +#include "wine/server.h" +#include "wine/unicode.h" +#include "wine/debug.h" #include "ntdll_misc.h" WINE_DEFAULT_DEBUG_CHANNEL(ntdll); @@ -261,3 +277,174 @@ NTSTATUS WINAPI NtQueryEvent ( FIXME("(%p)\n", EventHandle); return STATUS_SUCCESS; } + + +/*********************************************************************** + * check_async_list + * + * Process a status event from the server. + */ +static void WINAPI check_async_list(async_private *asp, DWORD status) +{ + async_private *ovp; + DWORD ovp_status; + + for( ovp = NtCurrentTeb()->pending_list; ovp && ovp != asp; ovp = ovp->next ); + + if(!ovp) + return; + + if( status != STATUS_ALERTED ) + { + ovp_status = status; + ovp->ops->set_status (ovp, status); + } + else ovp_status = ovp->ops->get_status (ovp); + + if( ovp_status == STATUS_PENDING ) ovp->func( ovp ); + + /* This will destroy all but PENDING requests */ + register_old_async( ovp ); +} + + +/*********************************************************************** + * wait_reply + * + * Wait for a reply on the waiting pipe of the current thread. + */ +static int wait_reply( void *cookie ) +{ + int signaled; + struct wake_up_reply reply; + for (;;) + { + int ret; + ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) ); + if (ret == sizeof(reply)) + { + if (!reply.cookie) break; /* thread got killed */ + if (reply.cookie == cookie) return reply.signaled; + /* we stole another reply, wait for the real one */ + signaled = wait_reply( cookie ); + /* and now put the wrong one back in the pipe */ + for (;;) + { + ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) ); + if (ret == sizeof(reply)) break; + if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret ); + if (errno == EINTR) continue; + server_protocol_perror("wakeup write"); + } + return signaled; + } + if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret ); + if (errno == EINTR) continue; + server_protocol_perror("wakeup read"); + } + /* the server closed the connection; time to die... */ + SYSDEPS_AbortThread(0); +} + + +/*********************************************************************** + * call_apcs + * + * Call outstanding APCs. + */ +static void call_apcs( BOOL alertable ) +{ + FARPROC proc = NULL; + LARGE_INTEGER time; + void *args[4]; + + for (;;) + { + int type = APC_NONE; + SERVER_START_REQ( get_apc ) + { + req->alertable = alertable; + wine_server_set_reply( req, args, sizeof(args) ); + if (!wine_server_call( req )) + { + type = reply->type; + proc = reply->func; + } + } + SERVER_END_REQ; + + switch(type) + { + case APC_NONE: + return; /* no more APCs */ + case APC_ASYNC: + proc( args[0], args[1]); + break; + case APC_USER: + proc( args[0] ); + break; + case APC_TIMER: + /* convert sec/usec to NT time */ + RtlSecondsSince1970ToTime( (time_t)args[0], &time ); + time.QuadPart += (DWORD)args[1] * 10; + proc( args[2], time.s.LowPart, time.s.HighPart ); + break; + case APC_ASYNC_IO: + check_async_list ( args[0], (DWORD) args[1]); + break; + default: + server_protocol_error( "get_apc_request: bad type %d\n", type ); + break; + } + } +} + +/* wait operations */ + +/****************************************************************** + * NtWaitForMultipleObjects (NTDLL.@) + */ +NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, + BOOLEAN wait_all, BOOLEAN alertable, + PLARGE_INTEGER timeout ) +{ + int ret, cookie; + struct timeval tv; + + if (count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; + + NTDLL_get_server_timeout( &tv, timeout ); + + for (;;) + { + SERVER_START_REQ( select ) + { + req->flags = SELECT_INTERRUPTIBLE; + req->cookie = &cookie; + req->sec = tv.tv_sec; + req->usec = tv.tv_usec; + wine_server_add_data( req, handles, count * sizeof(HANDLE) ); + + if (wait_all) req->flags |= SELECT_ALL; + if (alertable) req->flags |= SELECT_ALERTABLE; + if (timeout) req->flags |= SELECT_TIMEOUT; + + ret = wine_server_call( req ); + } + SERVER_END_REQ; + if (ret == STATUS_PENDING) ret = wait_reply( &cookie ); + if (ret != STATUS_USER_APC) break; + call_apcs( alertable ); + if (alertable) break; + } + return ret; +} + + +/****************************************************************** + * NtWaitForSingleObject (NTDLL.@) + */ +NTSTATUS WINAPI NtWaitForSingleObject(HANDLE handle, BOOLEAN alertable, PLARGE_INTEGER timeout ) +{ + return NtWaitForMultipleObjects( 1, &handle, FALSE, alertable, timeout ); +} diff --git a/dlls/ntdll/time.c b/dlls/ntdll/time.c index d6efb0a0923..22805ddedf4 100644 --- a/dlls/ntdll/time.c +++ b/dlls/ntdll/time.c @@ -269,9 +269,11 @@ static const struct tagTZ_INFO TZ_INFO[] = #define MONSPERYEAR 12 /* 1601 to 1970 is 369 years plus 89 leap days */ -#define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)86400) +#define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY) +#define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC) /* 1601 to 1980 is 379 years plus 91 leap days */ -#define SECS_1601_to_1980 ((379 * 365 + 91) * (ULONGLONG)86400) +#define SECS_1601_TO_1980 ((379 * 365 + 91) * (ULONGLONG)SECSPERDAY) +#define TICKS_1601_TO_1980 (SECS_1601_TO_1980 * TICKSPERSEC) static const int YearLengths[2] = {DAYSPERNORMALYEAR, DAYSPERLEAPYEAR}; @@ -292,6 +294,39 @@ static inline void NormalizeTimeFields(CSHORT *FieldToNormalize, CSHORT *CarryFi *CarryField = (CSHORT) (*CarryField + 1); } +/*********************************************************************** + * NTDLL_get_server_timeout + * + * Convert a NTDLL timeout into a timeval struct to send to the server. + */ +void NTDLL_get_server_timeout( struct timeval *when, const LARGE_INTEGER *timeout ) +{ + UINT remainder; + + if (!timeout) /* infinite timeout */ + { + when->tv_sec = when->tv_usec = 0; + } + else if (timeout->QuadPart <= 0) /* relative timeout */ + { + ULONG sec = RtlEnlargedUnsignedDivide( -timeout->QuadPart, TICKSPERSEC, &remainder ); + gettimeofday( when, 0 ); + if ((when->tv_usec += remainder / 10) >= 1000000) + { + when->tv_usec -= 1000000; + when->tv_sec++; + } + when->tv_sec += sec; + } + else /* absolute time */ + { + when->tv_sec = RtlEnlargedUnsignedDivide( timeout->QuadPart - TICKS_1601_TO_1970, + TICKSPERSEC, &remainder ); + when->tv_usec = remainder / 10; + } +} + + /****************************************************************************** * RtlTimeToTimeFields [NTDLL.@] * @@ -501,7 +536,7 @@ BOOLEAN WINAPI RtlTimeToSecondsSince1980( const LARGE_INTEGER *time, LPDWORD res { ULONGLONG tmp = ((ULONGLONG)time->s.HighPart << 32) | time->s.LowPart; tmp = RtlLargeIntegerDivide( tmp, 10000000, NULL ); - tmp -= SECS_1601_to_1980; + tmp -= SECS_1601_TO_1980; if (tmp > 0xffffffff) return FALSE; *res = (DWORD)tmp; return TRUE; @@ -521,7 +556,7 @@ BOOLEAN WINAPI RtlTimeToSecondsSince1980( const LARGE_INTEGER *time, LPDWORD res */ void WINAPI RtlSecondsSince1970ToTime( DWORD time, LARGE_INTEGER *res ) { - ULONGLONG secs = RtlExtendedIntegerMultiply( time + SECS_1601_TO_1970, 10000000 ); + ULONGLONG secs = time * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970; res->s.LowPart = (DWORD)secs; res->s.HighPart = (DWORD)(secs >> 32); } @@ -540,7 +575,7 @@ void WINAPI RtlSecondsSince1970ToTime( DWORD time, LARGE_INTEGER *res ) */ void WINAPI RtlSecondsSince1980ToTime( DWORD time, LARGE_INTEGER *res ) { - ULONGLONG secs = RtlExtendedIntegerMultiply( time + SECS_1601_to_1980, 10000000 ); + ULONGLONG secs = time * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1980; res->s.LowPart = (DWORD)secs; res->s.HighPart = (DWORD)(secs >> 32); } @@ -588,7 +623,8 @@ NTSTATUS WINAPI NtQuerySystemTime( PLARGE_INTEGER time ) struct timeval now; gettimeofday( &now, 0 ); - time->QuadPart = RtlExtendedIntegerMultiply( now.tv_sec+SECS_1601_TO_1970, 10000000 ) + now.tv_usec * 10; + time->QuadPart = now.tv_sec * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970; + time->QuadPart += now.tv_usec * 10; return STATUS_SUCCESS; } diff --git a/include/winternl.h b/include/winternl.h index cbc48ca294c..a399e481cfc 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -872,6 +872,7 @@ NTSTATUS WINAPI NtUnloadKey(HKEY); NTSTATUS WINAPI NtUnlockVirtualMemory(HANDLE,PVOID*,ULONG*,ULONG); NTSTATUS WINAPI NtUnmapViewOfSection(HANDLE,PVOID); NTSTATUS WINAPI NtWaitForSingleObject(HANDLE,BOOLEAN,PLARGE_INTEGER); +NTSTATUS WINAPI NtWaitForMultipleObjects(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,PLARGE_INTEGER); void WINAPI RtlAcquirePebLock(void); BYTE WINAPI RtlAcquireResourceExclusive(LPRTL_RWLOCK,BYTE); diff --git a/scheduler/synchro.c b/scheduler/synchro.c index bbf67525eda..22bed1b51fe 100644 --- a/scheduler/synchro.c +++ b/scheduler/synchro.c @@ -20,163 +20,9 @@ #include "config.h" -#include -#include -#include -#ifdef HAVE_SYS_TIME_H -# include -#endif -#ifdef HAVE_SYS_POLL_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif -#include +#include "winbase.h" +#include "winternl.h" -#include "file.h" /* for DOSFS_UnixTimeToFileTime */ -#include "thread.h" -#include "winerror.h" -#include "wine/server.h" -#include "async.h" - - -/*********************************************************************** - * get_timeout - */ -inline static void get_timeout( struct timeval *when, int timeout ) -{ - gettimeofday( when, 0 ); - if (timeout) - { - long sec = timeout / 1000; - if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000) - { - when->tv_usec -= 1000000; - when->tv_sec++; - } - when->tv_sec += sec; - } -} - -/*********************************************************************** - * check_async_list - * - * Process a status event from the server. - */ -static void WINAPI check_async_list(async_private *asp, DWORD status) -{ - async_private *ovp; - DWORD ovp_status; - - for( ovp = NtCurrentTeb()->pending_list; ovp && ovp != asp; ovp = ovp->next ); - - if(!ovp) - return; - - if( status != STATUS_ALERTED ) - { - ovp_status = status; - ovp->ops->set_status (ovp, status); - } - else ovp_status = ovp->ops->get_status (ovp); - - if( ovp_status == STATUS_PENDING ) ovp->func( ovp ); - - /* This will destroy all but PENDING requests */ - register_old_async( ovp ); -} - - -/*********************************************************************** - * wait_reply - * - * Wait for a reply on the waiting pipe of the current thread. - */ -static int wait_reply( void *cookie ) -{ - int signaled; - struct wake_up_reply reply; - for (;;) - { - int ret; - ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) ); - if (ret == sizeof(reply)) - { - if (!reply.cookie) break; /* thread got killed */ - if (reply.cookie == cookie) return reply.signaled; - /* we stole another reply, wait for the real one */ - signaled = wait_reply( cookie ); - /* and now put the wrong one back in the pipe */ - for (;;) - { - ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) ); - if (ret == sizeof(reply)) break; - if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret ); - if (errno == EINTR) continue; - server_protocol_perror("wakeup write"); - } - return signaled; - } - if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret ); - if (errno == EINTR) continue; - server_protocol_perror("wakeup read"); - } - /* the server closed the connection; time to die... */ - SYSDEPS_AbortThread(0); -} - - -/*********************************************************************** - * call_apcs - * - * Call outstanding APCs. - */ -static void call_apcs( BOOL alertable ) -{ - FARPROC proc = NULL; - FILETIME ft; - void *args[4]; - - for (;;) - { - int type = APC_NONE; - SERVER_START_REQ( get_apc ) - { - req->alertable = alertable; - wine_server_set_reply( req, args, sizeof(args) ); - if (!wine_server_call( req )) - { - type = reply->type; - proc = reply->func; - } - } - SERVER_END_REQ; - - switch(type) - { - case APC_NONE: - return; /* no more APCs */ - case APC_ASYNC: - proc( args[0], args[1]); - break; - case APC_USER: - proc( args[0] ); - break; - case APC_TIMER: - /* convert sec/usec to NT time */ - DOSFS_UnixTimeToFileTime( (time_t)args[0], &ft, (DWORD)args[1] * 10 ); - proc( args[2], ft.dwLowDateTime, ft.dwHighDateTime ); - break; - case APC_ASYNC_IO: - check_async_list ( args[0], (DWORD) args[1]); - break; - default: - server_protocol_error( "get_apc_request: bad type %d\n", type ); - break; - } - } -} /*********************************************************************** * Sleep (KERNEL32.@) @@ -233,46 +79,27 @@ DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles, BOOL wait_all, DWORD timeout, BOOL alertable ) { - int ret, cookie; - struct timeval tv; + NTSTATUS status; - if (count > MAXIMUM_WAIT_OBJECTS) + if (timeout == INFINITE) { - SetLastError( ERROR_INVALID_PARAMETER ); - return WAIT_FAILED; + status = NtWaitForMultipleObjects( count, handles, wait_all, alertable, NULL ); + } + else + { + LARGE_INTEGER time; + + time.QuadPart = timeout * (ULONGLONG)10000; + time.QuadPart = -time.QuadPart; + status = NtWaitForMultipleObjects( count, handles, wait_all, alertable, &time ); } - if (timeout == INFINITE) tv.tv_sec = tv.tv_usec = 0; - else get_timeout( &tv, timeout ); - - for (;;) + if (HIWORD(status)) /* is it an error code? */ { - SERVER_START_REQ( select ) - { - req->flags = SELECT_INTERRUPTIBLE; - req->cookie = &cookie; - req->sec = tv.tv_sec; - req->usec = tv.tv_usec; - wine_server_add_data( req, handles, count * sizeof(HANDLE) ); - - if (wait_all) req->flags |= SELECT_ALL; - if (alertable) req->flags |= SELECT_ALERTABLE; - if (timeout != INFINITE) req->flags |= SELECT_TIMEOUT; - - ret = wine_server_call( req ); - } - SERVER_END_REQ; - if (ret == STATUS_PENDING) ret = wait_reply( &cookie ); - if (ret != STATUS_USER_APC) break; - call_apcs( alertable ); - if (alertable) break; + SetLastError( RtlNtStatusToDosError(status) ); + status = WAIT_FAILED; } - if (HIWORD(ret)) /* is it an error code? */ - { - SetLastError( RtlNtStatusToDosError(ret) ); - ret = WAIT_FAILED; - } - return ret; + return status; } diff --git a/scheduler/timer.c b/scheduler/timer.c index b67563c2d1c..ee6e9072165 100644 --- a/scheduler/timer.c +++ b/scheduler/timer.c @@ -29,9 +29,9 @@ #include "winerror.h" #include "winnls.h" #include "wine/unicode.h" -#include "file.h" /* for FILETIME routines */ #include "wine/server.h" #include "wine/debug.h" +#include "ntdll_misc.h" WINE_DEFAULT_DEBUG_CHANNEL(timer); @@ -130,34 +130,21 @@ BOOL WINAPI SetWaitableTimer( HANDLE handle, const LARGE_INTEGER *when, LONG per PTIMERAPCROUTINE callback, LPVOID arg, BOOL resume ) { BOOL ret; - LARGE_INTEGER exp = *when; + struct timeval tv; - if (exp.s.HighPart < 0) /* relative time */ + if (!when->s.LowPart && !when->s.HighPart) { - LARGE_INTEGER now; - NtQuerySystemTime( &now ); - exp.QuadPart = RtlLargeIntegerSubtract( now.QuadPart, exp.QuadPart ); + /* special case to start timeout on now+period without too many calculations */ + tv.tv_sec = 0; + tv.tv_usec = 0; } + else NTDLL_get_server_timeout( &tv, when ); SERVER_START_REQ( set_timer ) { - if (!exp.s.LowPart && !exp.s.HighPart) - { - /* special case to start timeout on now+period without too many calculations */ - req->sec = 0; - req->usec = 0; - } - else - { - DWORD remainder; - FILETIME ft; - - ft.dwLowDateTime = exp.s.LowPart; - ft.dwHighDateTime = exp.s.HighPart; - req->sec = DOSFS_FileTimeToUnixTime( &ft, &remainder ); - req->usec = remainder / 10; /* convert from 100-ns to us units */ - } req->handle = handle; + req->sec = tv.tv_sec; + req->usec = tv.tv_usec; req->period = period; req->callback = callback; req->arg = arg;