
This makes sure that system APC, such as APC_BREAK_PROCESS do not get interrupted in the middle of their execution, and that the APC completion notification is always correctly sent back to the caller. Otherwise DbgBreakProcess sometimes did not return until WaitForDebugEvent/ContinueDebugEvent are called, because of a race condition between the APC servicing thread, and the newly created exception thread. Signed-off-by: Rémi Bernon <rbernon@codeweavers.com> Signed-off-by: Jacek Caban <jacek@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2551 lines
80 KiB
C
2551 lines
80 KiB
C
/*
|
|
* Process synchronisation
|
|
*
|
|
* Copyright 1996, 1997, 1998 Marcus Meissner
|
|
* Copyright 1997, 1999 Alexandre Julliard
|
|
* Copyright 1999, 2000 Juergen Schmied
|
|
* Copyright 2003 Eric Pouech
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#ifdef HAVE_SYS_SYSCALL_H
|
|
#include <sys/syscall.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
#endif
|
|
#ifdef HAVE_POLL_H
|
|
#include <poll.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_POLL_H
|
|
# include <sys/poll.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_SCHED_H
|
|
# include <sched.h>
|
|
#endif
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#define NONAMELESSUNION
|
|
#include "windef.h"
|
|
#include "winternl.h"
|
|
#include "wine/server.h"
|
|
#include "wine/debug.h"
|
|
#include "ntdll_misc.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(sync);
|
|
|
|
HANDLE keyed_event = NULL;
|
|
|
|
static const LARGE_INTEGER zero_timeout;
|
|
|
|
#define TICKSPERSEC 10000000
|
|
|
|
#ifdef __linux__
|
|
|
|
#define FUTEX_WAIT 0
|
|
#define FUTEX_WAKE 1
|
|
#define FUTEX_WAIT_BITSET 9
|
|
#define FUTEX_WAKE_BITSET 10
|
|
|
|
static int futex_private = 128;
|
|
|
|
static inline int futex_wait( const int *addr, int val, struct timespec *timeout )
|
|
{
|
|
return syscall( __NR_futex, addr, FUTEX_WAIT | futex_private, val, timeout, 0, 0 );
|
|
}
|
|
|
|
static inline int futex_wake( const int *addr, int val )
|
|
{
|
|
return syscall( __NR_futex, addr, FUTEX_WAKE | futex_private, val, NULL, 0, 0 );
|
|
}
|
|
|
|
static inline int futex_wait_bitset( const int *addr, int val, struct timespec *timeout, int mask )
|
|
{
|
|
return syscall( __NR_futex, addr, FUTEX_WAIT_BITSET | futex_private, val, timeout, 0, mask );
|
|
}
|
|
|
|
static inline int futex_wake_bitset( const int *addr, int val, int mask )
|
|
{
|
|
return syscall( __NR_futex, addr, FUTEX_WAKE_BITSET | futex_private, val, NULL, 0, mask );
|
|
}
|
|
|
|
static inline int use_futexes(void)
|
|
{
|
|
static int supported = -1;
|
|
|
|
if (supported == -1)
|
|
{
|
|
futex_wait( &supported, 10, NULL );
|
|
if (errno == ENOSYS)
|
|
{
|
|
futex_private = 0;
|
|
futex_wait( &supported, 10, NULL );
|
|
}
|
|
supported = (errno != ENOSYS);
|
|
}
|
|
return supported;
|
|
}
|
|
|
|
static void timespec_from_timeout( struct timespec *timespec, const LARGE_INTEGER *timeout )
|
|
{
|
|
LARGE_INTEGER now;
|
|
timeout_t diff;
|
|
|
|
if (timeout->QuadPart > 0)
|
|
{
|
|
NtQuerySystemTime( &now );
|
|
diff = timeout->QuadPart - now.QuadPart;
|
|
}
|
|
else
|
|
diff = -timeout->QuadPart;
|
|
|
|
timespec->tv_sec = diff / TICKSPERSEC;
|
|
timespec->tv_nsec = (diff % TICKSPERSEC) * 100;
|
|
}
|
|
#endif
|
|
|
|
/* creates a struct security_descriptor and contained information in one contiguous piece of memory */
|
|
NTSTATUS alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct object_attributes **ret,
|
|
data_size_t *ret_len )
|
|
{
|
|
unsigned int len = sizeof(**ret);
|
|
PSID owner = NULL, group = NULL;
|
|
ACL *dacl, *sacl;
|
|
BOOLEAN dacl_present, sacl_present, defaulted;
|
|
PSECURITY_DESCRIPTOR sd;
|
|
NTSTATUS status;
|
|
|
|
*ret = NULL;
|
|
*ret_len = 0;
|
|
|
|
if (!attr) return STATUS_SUCCESS;
|
|
|
|
if (attr->Length != sizeof(*attr)) return STATUS_INVALID_PARAMETER;
|
|
|
|
if ((sd = attr->SecurityDescriptor))
|
|
{
|
|
len += sizeof(struct security_descriptor);
|
|
|
|
if ((status = RtlGetOwnerSecurityDescriptor( sd, &owner, &defaulted ))) return status;
|
|
if ((status = RtlGetGroupSecurityDescriptor( sd, &group, &defaulted ))) return status;
|
|
if ((status = RtlGetSaclSecurityDescriptor( sd, &sacl_present, &sacl, &defaulted ))) return status;
|
|
if ((status = RtlGetDaclSecurityDescriptor( sd, &dacl_present, &dacl, &defaulted ))) return status;
|
|
if (owner) len += RtlLengthSid( owner );
|
|
if (group) len += RtlLengthSid( group );
|
|
if (sacl_present && sacl) len += sacl->AclSize;
|
|
if (dacl_present && dacl) len += dacl->AclSize;
|
|
|
|
/* fix alignment for the Unicode name that follows the structure */
|
|
len = (len + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1);
|
|
}
|
|
|
|
if (attr->ObjectName)
|
|
{
|
|
if (attr->ObjectName->Length & (sizeof(WCHAR) - 1)) return STATUS_OBJECT_NAME_INVALID;
|
|
len += attr->ObjectName->Length;
|
|
}
|
|
else if (attr->RootDirectory) return STATUS_OBJECT_NAME_INVALID;
|
|
|
|
len = (len + 3) & ~3; /* DWORD-align the entire structure */
|
|
|
|
*ret = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, len );
|
|
if (!*ret) return STATUS_NO_MEMORY;
|
|
|
|
(*ret)->rootdir = wine_server_obj_handle( attr->RootDirectory );
|
|
(*ret)->attributes = attr->Attributes;
|
|
|
|
if (attr->SecurityDescriptor)
|
|
{
|
|
struct security_descriptor *descr = (struct security_descriptor *)(*ret + 1);
|
|
unsigned char *ptr = (unsigned char *)(descr + 1);
|
|
|
|
descr->control = ((SECURITY_DESCRIPTOR *)sd)->Control & ~SE_SELF_RELATIVE;
|
|
if (owner) descr->owner_len = RtlLengthSid( owner );
|
|
if (group) descr->group_len = RtlLengthSid( group );
|
|
if (sacl_present && sacl) descr->sacl_len = sacl->AclSize;
|
|
if (dacl_present && dacl) descr->dacl_len = dacl->AclSize;
|
|
|
|
memcpy( ptr, owner, descr->owner_len );
|
|
ptr += descr->owner_len;
|
|
memcpy( ptr, group, descr->group_len );
|
|
ptr += descr->group_len;
|
|
memcpy( ptr, sacl, descr->sacl_len );
|
|
ptr += descr->sacl_len;
|
|
memcpy( ptr, dacl, descr->dacl_len );
|
|
(*ret)->sd_len = (sizeof(*descr) + descr->owner_len + descr->group_len + descr->sacl_len +
|
|
descr->dacl_len + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1);
|
|
}
|
|
|
|
if (attr->ObjectName)
|
|
{
|
|
unsigned char *ptr = (unsigned char *)(*ret + 1) + (*ret)->sd_len;
|
|
(*ret)->name_len = attr->ObjectName->Length;
|
|
memcpy( ptr, attr->ObjectName->Buffer, (*ret)->name_len );
|
|
}
|
|
|
|
*ret_len = len;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
if (!attr || attr->Length != sizeof(*attr)) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (attr->ObjectName)
|
|
{
|
|
if (attr->ObjectName->Length & (sizeof(WCHAR) - 1)) return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
else if (attr->RootDirectory) return STATUS_OBJECT_NAME_INVALID;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Semaphores
|
|
*/
|
|
|
|
/******************************************************************************
|
|
* NtCreateSemaphore (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtCreateSemaphore( OUT PHANDLE SemaphoreHandle,
|
|
IN ACCESS_MASK access,
|
|
IN const OBJECT_ATTRIBUTES *attr OPTIONAL,
|
|
IN LONG InitialCount,
|
|
IN LONG MaximumCount )
|
|
{
|
|
NTSTATUS ret;
|
|
data_size_t len;
|
|
struct object_attributes *objattr;
|
|
|
|
if (MaximumCount <= 0 || InitialCount < 0 || InitialCount > MaximumCount)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
|
|
|
|
SERVER_START_REQ( create_semaphore )
|
|
{
|
|
req->access = access;
|
|
req->initial = InitialCount;
|
|
req->max = MaximumCount;
|
|
wine_server_add_data( req, objattr, len );
|
|
ret = wine_server_call( req );
|
|
*SemaphoreHandle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
RtlFreeHeap( GetProcessHeap(), 0, objattr );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtOpenSemaphore (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
if ((ret = validate_open_object_attributes( attr ))) return ret;
|
|
|
|
SERVER_START_REQ( open_semaphore )
|
|
{
|
|
req->access = access;
|
|
req->attributes = attr->Attributes;
|
|
req->rootdir = wine_server_obj_handle( attr->RootDirectory );
|
|
if (attr->ObjectName)
|
|
wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
|
|
ret = wine_server_call( req );
|
|
*handle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtQuerySemaphore (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS class,
|
|
void *info, ULONG len, ULONG *ret_len )
|
|
{
|
|
NTSTATUS ret;
|
|
SEMAPHORE_BASIC_INFORMATION *out = info;
|
|
|
|
TRACE("(%p, %u, %p, %u, %p)\n", handle, class, info, len, ret_len);
|
|
|
|
if (class != SemaphoreBasicInformation)
|
|
{
|
|
FIXME("(%p,%d,%u) Unknown class\n", handle, class, len);
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
if (len != sizeof(SEMAPHORE_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
SERVER_START_REQ( query_semaphore )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
if (!(ret = wine_server_call( req )))
|
|
{
|
|
out->CurrentCount = reply->current;
|
|
out->MaximumCount = reply->max;
|
|
if (ret_len) *ret_len = sizeof(SEMAPHORE_BASIC_INFORMATION);
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtReleaseSemaphore (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous )
|
|
{
|
|
NTSTATUS ret;
|
|
SERVER_START_REQ( release_semaphore )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
req->count = count;
|
|
if (!(ret = wine_server_call( req )))
|
|
{
|
|
if (previous) *previous = reply->prev_count;
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Events
|
|
*/
|
|
|
|
/**************************************************************************
|
|
* NtCreateEvent (NTDLL.@)
|
|
* ZwCreateEvent (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtCreateEvent( PHANDLE EventHandle, ACCESS_MASK DesiredAccess,
|
|
const OBJECT_ATTRIBUTES *attr, EVENT_TYPE type, BOOLEAN InitialState)
|
|
{
|
|
NTSTATUS ret;
|
|
data_size_t len;
|
|
struct object_attributes *objattr;
|
|
|
|
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
|
|
|
|
SERVER_START_REQ( create_event )
|
|
{
|
|
req->access = DesiredAccess;
|
|
req->manual_reset = (type == NotificationEvent);
|
|
req->initial_state = InitialState;
|
|
wine_server_add_data( req, objattr, len );
|
|
ret = wine_server_call( req );
|
|
*EventHandle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
RtlFreeHeap( GetProcessHeap(), 0, objattr );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtOpenEvent (NTDLL.@)
|
|
* ZwOpenEvent (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
if ((ret = validate_open_object_attributes( attr ))) return ret;
|
|
|
|
SERVER_START_REQ( open_event )
|
|
{
|
|
req->access = access;
|
|
req->attributes = attr->Attributes;
|
|
req->rootdir = wine_server_obj_handle( attr->RootDirectory );
|
|
if (attr->ObjectName)
|
|
wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
|
|
ret = wine_server_call( req );
|
|
*handle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* NtSetEvent (NTDLL.@)
|
|
* ZwSetEvent (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state )
|
|
{
|
|
NTSTATUS ret;
|
|
SERVER_START_REQ( event_op )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
req->op = SET_EVENT;
|
|
ret = wine_server_call( req );
|
|
if (!ret && prev_state) *prev_state = reply->state;
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtResetEvent (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state )
|
|
{
|
|
NTSTATUS ret;
|
|
SERVER_START_REQ( event_op )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
req->op = RESET_EVENT;
|
|
ret = wine_server_call( req );
|
|
if (!ret && prev_state) *prev_state = reply->state;
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtClearEvent (NTDLL.@)
|
|
*
|
|
* FIXME
|
|
* same as NtResetEvent ???
|
|
*/
|
|
NTSTATUS WINAPI NtClearEvent ( HANDLE handle )
|
|
{
|
|
return NtResetEvent( handle, NULL );
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtPulseEvent (NTDLL.@)
|
|
*
|
|
* FIXME
|
|
* PulseCount
|
|
*/
|
|
NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
SERVER_START_REQ( event_op )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
req->op = PULSE_EVENT;
|
|
ret = wine_server_call( req );
|
|
if (!ret && prev_state) *prev_state = reply->state;
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtQueryEvent (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class,
|
|
void *info, ULONG len, ULONG *ret_len )
|
|
{
|
|
NTSTATUS ret;
|
|
EVENT_BASIC_INFORMATION *out = info;
|
|
|
|
TRACE("(%p, %u, %p, %u, %p)\n", handle, class, info, len, ret_len);
|
|
|
|
if (class != EventBasicInformation)
|
|
{
|
|
FIXME("(%p, %d, %d) Unknown class\n",
|
|
handle, class, len);
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
if (len != sizeof(EVENT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
SERVER_START_REQ( query_event )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
if (!(ret = wine_server_call( req )))
|
|
{
|
|
out->EventType = reply->manual_reset ? NotificationEvent : SynchronizationEvent;
|
|
out->EventState = reply->state;
|
|
if (ret_len) *ret_len = sizeof(EVENT_BASIC_INFORMATION);
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Mutants (known as Mutexes in Kernel32)
|
|
*/
|
|
|
|
/******************************************************************************
|
|
* NtCreateMutant [NTDLL.@]
|
|
* ZwCreateMutant [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtCreateMutant(OUT HANDLE* MutantHandle,
|
|
IN ACCESS_MASK access,
|
|
IN const OBJECT_ATTRIBUTES* attr OPTIONAL,
|
|
IN BOOLEAN InitialOwner)
|
|
{
|
|
NTSTATUS status;
|
|
data_size_t len;
|
|
struct object_attributes *objattr;
|
|
|
|
if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
|
|
|
|
SERVER_START_REQ( create_mutex )
|
|
{
|
|
req->access = access;
|
|
req->owned = InitialOwner;
|
|
wine_server_add_data( req, objattr, len );
|
|
status = wine_server_call( req );
|
|
*MutantHandle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
RtlFreeHeap( GetProcessHeap(), 0, objattr );
|
|
return status;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* NtOpenMutant [NTDLL.@]
|
|
* ZwOpenMutant [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
if ((status = validate_open_object_attributes( attr ))) return status;
|
|
|
|
SERVER_START_REQ( open_mutex )
|
|
{
|
|
req->access = access;
|
|
req->attributes = attr->Attributes;
|
|
req->rootdir = wine_server_obj_handle( attr->RootDirectory );
|
|
if (attr->ObjectName)
|
|
wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
|
|
status = wine_server_call( req );
|
|
*handle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
return status;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* NtReleaseMutant [NTDLL.@]
|
|
* ZwReleaseMutant [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtReleaseMutant( IN HANDLE handle, OUT PLONG prev_count OPTIONAL)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
SERVER_START_REQ( release_mutex )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
status = wine_server_call( req );
|
|
if (prev_count) *prev_count = 1 - reply->prev_count;
|
|
}
|
|
SERVER_END_REQ;
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************
|
|
* NtQueryMutant [NTDLL.@]
|
|
* ZwQueryMutant [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class,
|
|
void *info, ULONG len, ULONG *ret_len )
|
|
{
|
|
NTSTATUS ret;
|
|
MUTANT_BASIC_INFORMATION *out = info;
|
|
|
|
TRACE("(%p, %u, %p, %u, %p)\n", handle, class, info, len, ret_len);
|
|
|
|
if (class != MutantBasicInformation)
|
|
{
|
|
FIXME("(%p, %d, %d) Unknown class\n",
|
|
handle, class, len);
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
if (len != sizeof(MUTANT_BASIC_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
SERVER_START_REQ( query_mutex )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
if (!(ret = wine_server_call( req )))
|
|
{
|
|
out->CurrentCount = 1 - reply->count;
|
|
out->OwnedByCaller = reply->owned;
|
|
out->AbandonedState = reply->abandoned;
|
|
if (ret_len) *ret_len = sizeof(MUTANT_BASIC_INFORMATION);
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Jobs
|
|
*/
|
|
|
|
/******************************************************************************
|
|
* NtCreateJobObject [NTDLL.@]
|
|
* ZwCreateJobObject [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtCreateJobObject( PHANDLE handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
NTSTATUS ret;
|
|
data_size_t len;
|
|
struct object_attributes *objattr;
|
|
|
|
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
|
|
|
|
SERVER_START_REQ( create_job )
|
|
{
|
|
req->access = access;
|
|
wine_server_add_data( req, objattr, len );
|
|
ret = wine_server_call( req );
|
|
*handle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
RtlFreeHeap( GetProcessHeap(), 0, objattr );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtOpenJobObject [NTDLL.@]
|
|
* ZwOpenJobObject [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtOpenJobObject( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
if ((ret = validate_open_object_attributes( attr ))) return ret;
|
|
|
|
SERVER_START_REQ( open_job )
|
|
{
|
|
req->access = access;
|
|
req->attributes = attr->Attributes;
|
|
req->rootdir = wine_server_obj_handle( attr->RootDirectory );
|
|
if (attr->ObjectName)
|
|
wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
|
|
ret = wine_server_call( req );
|
|
*handle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtTerminateJobObject [NTDLL.@]
|
|
* ZwTerminateJobObject [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtTerminateJobObject( HANDLE handle, NTSTATUS status )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
TRACE( "(%p, %d)\n", handle, status );
|
|
|
|
SERVER_START_REQ( terminate_job )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
req->status = status;
|
|
ret = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtQueryInformationJobObject [NTDLL.@]
|
|
* ZwQueryInformationJobObject [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtQueryInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS class, PVOID info,
|
|
ULONG len, PULONG ret_len )
|
|
{
|
|
FIXME( "stub: %p %u %p %u %p\n", handle, class, info, len, ret_len );
|
|
|
|
if (class >= MaxJobObjectInfoClass)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
switch (class)
|
|
{
|
|
case JobObjectBasicAccountingInformation:
|
|
{
|
|
JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *accounting;
|
|
if (len < sizeof(*accounting))
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
accounting = (JOBOBJECT_BASIC_ACCOUNTING_INFORMATION *)info;
|
|
memset(accounting, 0, sizeof(*accounting));
|
|
if (ret_len) *ret_len = sizeof(*accounting);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
case JobObjectBasicProcessIdList:
|
|
{
|
|
JOBOBJECT_BASIC_PROCESS_ID_LIST *process;
|
|
if (len < sizeof(*process))
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
process = (JOBOBJECT_BASIC_PROCESS_ID_LIST *)info;
|
|
memset(process, 0, sizeof(*process));
|
|
if (ret_len) *ret_len = sizeof(*process);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
case JobObjectExtendedLimitInformation:
|
|
{
|
|
JOBOBJECT_EXTENDED_LIMIT_INFORMATION *extended_limit;
|
|
if (len < sizeof(*extended_limit))
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
extended_limit = (JOBOBJECT_EXTENDED_LIMIT_INFORMATION *)info;
|
|
memset(extended_limit, 0, sizeof(*extended_limit));
|
|
if (ret_len) *ret_len = sizeof(*extended_limit);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
case JobObjectBasicLimitInformation:
|
|
{
|
|
JOBOBJECT_BASIC_LIMIT_INFORMATION *basic_limit;
|
|
if (len < sizeof(*basic_limit))
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
basic_limit = (JOBOBJECT_BASIC_LIMIT_INFORMATION *)info;
|
|
memset(basic_limit, 0, sizeof(*basic_limit));
|
|
if (ret_len) *ret_len = sizeof(*basic_limit);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
default:
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtSetInformationJobObject [NTDLL.@]
|
|
* ZwSetInformationJobObject [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtSetInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS class, PVOID info, ULONG len )
|
|
{
|
|
NTSTATUS status = STATUS_NOT_IMPLEMENTED;
|
|
JOBOBJECT_BASIC_LIMIT_INFORMATION *basic_limit;
|
|
ULONG info_size = sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION);
|
|
DWORD limit_flags = JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS;
|
|
|
|
TRACE( "(%p, %u, %p, %u)\n", handle, class, info, len );
|
|
|
|
if (class >= MaxJobObjectInfoClass)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
switch (class)
|
|
{
|
|
|
|
case JobObjectExtendedLimitInformation:
|
|
info_size = sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION);
|
|
limit_flags = JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS;
|
|
/* fallthrough */
|
|
case JobObjectBasicLimitInformation:
|
|
if (len != info_size)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
basic_limit = info;
|
|
if (basic_limit->LimitFlags & ~limit_flags)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
SERVER_START_REQ( set_job_limits )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
req->limit_flags = basic_limit->LimitFlags;
|
|
status = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
break;
|
|
|
|
case JobObjectAssociateCompletionPortInformation:
|
|
if (len != sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
SERVER_START_REQ( set_job_completion_port )
|
|
{
|
|
JOBOBJECT_ASSOCIATE_COMPLETION_PORT *port_info = info;
|
|
req->job = wine_server_obj_handle( handle );
|
|
req->port = wine_server_obj_handle( port_info->CompletionPort );
|
|
req->key = wine_server_client_ptr( port_info->CompletionKey );
|
|
status = wine_server_call(req);
|
|
}
|
|
SERVER_END_REQ;
|
|
break;
|
|
|
|
case JobObjectBasicUIRestrictions:
|
|
status = STATUS_SUCCESS;
|
|
/* fallthrough */
|
|
default:
|
|
FIXME( "stub: %p %u %p %u\n", handle, class, info, len );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtIsProcessInJob [NTDLL.@]
|
|
* ZwIsProcessInJob [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtIsProcessInJob( HANDLE process, HANDLE job )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
TRACE( "(%p %p)\n", job, process );
|
|
|
|
SERVER_START_REQ( process_in_job )
|
|
{
|
|
req->job = wine_server_obj_handle( job );
|
|
req->process = wine_server_obj_handle( process );
|
|
status = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtAssignProcessToJobObject [NTDLL.@]
|
|
* ZwAssignProcessToJobObject [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtAssignProcessToJobObject( HANDLE job, HANDLE process )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
TRACE( "(%p %p)\n", job, process );
|
|
|
|
SERVER_START_REQ( assign_job )
|
|
{
|
|
req->job = wine_server_obj_handle( job );
|
|
req->process = wine_server_obj_handle( process );
|
|
status = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Timers
|
|
*/
|
|
|
|
/**************************************************************************
|
|
* NtCreateTimer [NTDLL.@]
|
|
* ZwCreateTimer [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtCreateTimer(OUT HANDLE *handle,
|
|
IN ACCESS_MASK access,
|
|
IN const OBJECT_ATTRIBUTES *attr OPTIONAL,
|
|
IN TIMER_TYPE timer_type)
|
|
{
|
|
NTSTATUS status;
|
|
data_size_t len;
|
|
struct object_attributes *objattr;
|
|
|
|
if (timer_type != NotificationTimer && timer_type != SynchronizationTimer)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
|
|
|
|
SERVER_START_REQ( create_timer )
|
|
{
|
|
req->access = access;
|
|
req->manual = (timer_type == NotificationTimer);
|
|
wine_server_add_data( req, objattr, len );
|
|
status = wine_server_call( req );
|
|
*handle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
RtlFreeHeap( GetProcessHeap(), 0, objattr );
|
|
return status;
|
|
|
|
}
|
|
|
|
/**************************************************************************
|
|
* NtOpenTimer [NTDLL.@]
|
|
* ZwOpenTimer [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
if ((status = validate_open_object_attributes( attr ))) return status;
|
|
|
|
SERVER_START_REQ( open_timer )
|
|
{
|
|
req->access = access;
|
|
req->attributes = attr->Attributes;
|
|
req->rootdir = wine_server_obj_handle( attr->RootDirectory );
|
|
if (attr->ObjectName)
|
|
wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
|
|
status = wine_server_call( req );
|
|
*handle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
return status;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* NtSetTimer [NTDLL.@]
|
|
* ZwSetTimer [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtSetTimer(IN HANDLE handle,
|
|
IN const LARGE_INTEGER* when,
|
|
IN PTIMER_APC_ROUTINE callback,
|
|
IN PVOID callback_arg,
|
|
IN BOOLEAN resume,
|
|
IN ULONG period OPTIONAL,
|
|
OUT PBOOLEAN state OPTIONAL)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
TRACE("(%p,%p,%p,%p,%08x,0x%08x,%p)\n",
|
|
handle, when, callback, callback_arg, resume, period, state);
|
|
|
|
SERVER_START_REQ( set_timer )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
req->period = period;
|
|
req->expire = when->QuadPart;
|
|
req->callback = wine_server_client_ptr( callback );
|
|
req->arg = wine_server_client_ptr( callback_arg );
|
|
status = wine_server_call( req );
|
|
if (state) *state = reply->signaled;
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
/* set error but can still succeed */
|
|
if (resume && status == STATUS_SUCCESS) return STATUS_TIMER_RESUME_IGNORED;
|
|
return status;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* NtCancelTimer [NTDLL.@]
|
|
* ZwCancelTimer [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtCancelTimer(IN HANDLE handle, OUT BOOLEAN* state)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
SERVER_START_REQ( cancel_timer )
|
|
{
|
|
req->handle = wine_server_obj_handle( handle );
|
|
status = wine_server_call( req );
|
|
if (state) *state = reply->signaled;
|
|
}
|
|
SERVER_END_REQ;
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtQueryTimer (NTDLL.@)
|
|
*
|
|
* Retrieves information about a timer.
|
|
*
|
|
* PARAMS
|
|
* TimerHandle [I] The timer to retrieve information about.
|
|
* TimerInformationClass [I] The type of information to retrieve.
|
|
* TimerInformation [O] Pointer to buffer to store information in.
|
|
* Length [I] The length of the buffer pointed to by TimerInformation.
|
|
* ReturnLength [O] Optional. The size of buffer actually used.
|
|
*
|
|
* RETURNS
|
|
* Success: STATUS_SUCCESS
|
|
* Failure: STATUS_INFO_LENGTH_MISMATCH, if Length doesn't match the required data
|
|
* size for the class specified.
|
|
* STATUS_INVALID_INFO_CLASS, if an invalid TimerInformationClass was specified.
|
|
* STATUS_ACCESS_DENIED, if TimerHandle does not have TIMER_QUERY_STATE access
|
|
* to the timer.
|
|
*/
|
|
NTSTATUS WINAPI NtQueryTimer(
|
|
HANDLE TimerHandle,
|
|
TIMER_INFORMATION_CLASS TimerInformationClass,
|
|
PVOID TimerInformation,
|
|
ULONG Length,
|
|
PULONG ReturnLength)
|
|
{
|
|
TIMER_BASIC_INFORMATION * basic_info = TimerInformation;
|
|
NTSTATUS status;
|
|
LARGE_INTEGER now;
|
|
|
|
TRACE("(%p,%d,%p,0x%08x,%p)\n", TimerHandle, TimerInformationClass,
|
|
TimerInformation, Length, ReturnLength);
|
|
|
|
switch (TimerInformationClass)
|
|
{
|
|
case TimerBasicInformation:
|
|
if (Length < sizeof(TIMER_BASIC_INFORMATION))
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
SERVER_START_REQ(get_timer_info)
|
|
{
|
|
req->handle = wine_server_obj_handle( TimerHandle );
|
|
status = wine_server_call(req);
|
|
|
|
/* convert server time to absolute NTDLL time */
|
|
basic_info->RemainingTime.QuadPart = reply->when;
|
|
basic_info->TimerState = reply->signaled;
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
/* convert from absolute into relative time */
|
|
NtQuerySystemTime(&now);
|
|
if (now.QuadPart > basic_info->RemainingTime.QuadPart)
|
|
basic_info->RemainingTime.QuadPart = 0;
|
|
else
|
|
basic_info->RemainingTime.QuadPart -= now.QuadPart;
|
|
|
|
if (ReturnLength) *ReturnLength = sizeof(TIMER_BASIC_INFORMATION);
|
|
|
|
return status;
|
|
}
|
|
|
|
FIXME("Unhandled class %d\n", TimerInformationClass);
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* NtQueryTimerResolution [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtQueryTimerResolution(OUT ULONG* min_resolution,
|
|
OUT ULONG* max_resolution,
|
|
OUT ULONG* current_resolution)
|
|
{
|
|
FIXME("(%p,%p,%p), stub!\n",
|
|
min_resolution, max_resolution, current_resolution);
|
|
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtSetTimerResolution [NTDLL.@]
|
|
*/
|
|
NTSTATUS WINAPI NtSetTimerResolution(IN ULONG resolution,
|
|
IN BOOLEAN set_resolution,
|
|
OUT ULONG* current_resolution )
|
|
{
|
|
FIXME("(%u,%u,%p), stub!\n",
|
|
resolution, set_resolution, current_resolution);
|
|
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
|
|
/* wait operations */
|
|
|
|
static NTSTATUS wait_objects( DWORD count, const HANDLE *handles,
|
|
BOOLEAN wait_any, BOOLEAN alertable,
|
|
const LARGE_INTEGER *timeout )
|
|
{
|
|
select_op_t select_op;
|
|
UINT i, flags = SELECT_INTERRUPTIBLE;
|
|
|
|
if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1;
|
|
|
|
if (alertable) flags |= SELECT_ALERTABLE;
|
|
select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL;
|
|
for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] );
|
|
return server_select( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout );
|
|
}
|
|
|
|
|
|
/******************************************************************
|
|
* NtWaitForMultipleObjects (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles,
|
|
BOOLEAN wait_any, BOOLEAN alertable,
|
|
const LARGE_INTEGER *timeout )
|
|
{
|
|
return wait_objects( count, handles, wait_any, alertable, timeout );
|
|
}
|
|
|
|
|
|
/******************************************************************
|
|
* NtWaitForSingleObject (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtWaitForSingleObject(HANDLE handle, BOOLEAN alertable, const LARGE_INTEGER *timeout )
|
|
{
|
|
return wait_objects( 1, &handle, FALSE, alertable, timeout );
|
|
}
|
|
|
|
|
|
/******************************************************************
|
|
* NtSignalAndWaitForSingleObject (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE hSignalObject, HANDLE hWaitObject,
|
|
BOOLEAN alertable, const LARGE_INTEGER *timeout )
|
|
{
|
|
select_op_t select_op;
|
|
UINT flags = SELECT_INTERRUPTIBLE;
|
|
|
|
if (!hSignalObject) return STATUS_INVALID_HANDLE;
|
|
|
|
if (alertable) flags |= SELECT_ALERTABLE;
|
|
select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT;
|
|
select_op.signal_and_wait.wait = wine_server_obj_handle( hWaitObject );
|
|
select_op.signal_and_wait.signal = wine_server_obj_handle( hSignalObject );
|
|
return server_select( &select_op, sizeof(select_op.signal_and_wait), flags, timeout );
|
|
}
|
|
|
|
|
|
/******************************************************************
|
|
* NtYieldExecution (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtYieldExecution(void)
|
|
{
|
|
#ifdef HAVE_SCHED_YIELD
|
|
sched_yield();
|
|
return STATUS_SUCCESS;
|
|
#else
|
|
return STATUS_NO_YIELD_PERFORMED;
|
|
#endif
|
|
}
|
|
|
|
|
|
/******************************************************************
|
|
* NtDelayExecution (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout )
|
|
{
|
|
/* if alertable, we need to query the server */
|
|
if (alertable)
|
|
return server_select( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, timeout );
|
|
|
|
if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) /* sleep forever */
|
|
{
|
|
for (;;) select( 0, NULL, NULL, NULL, NULL );
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER now;
|
|
timeout_t when, diff;
|
|
|
|
if ((when = timeout->QuadPart) < 0)
|
|
{
|
|
NtQuerySystemTime( &now );
|
|
when = now.QuadPart - when;
|
|
}
|
|
|
|
/* Note that we yield after establishing the desired timeout */
|
|
NtYieldExecution();
|
|
if (!when) return STATUS_SUCCESS;
|
|
|
|
for (;;)
|
|
{
|
|
struct timeval tv;
|
|
NtQuerySystemTime( &now );
|
|
diff = (when - now.QuadPart + 9) / 10;
|
|
if (diff <= 0) break;
|
|
tv.tv_sec = diff / 1000000;
|
|
tv.tv_usec = diff % 1000000;
|
|
if (select( 0, NULL, NULL, NULL, &tv ) != -1) break;
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* NtCreateKeyedEvent (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access,
|
|
const OBJECT_ATTRIBUTES *attr, ULONG flags )
|
|
{
|
|
NTSTATUS ret;
|
|
data_size_t len;
|
|
struct object_attributes *objattr;
|
|
|
|
if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret;
|
|
|
|
SERVER_START_REQ( create_keyed_event )
|
|
{
|
|
req->access = access;
|
|
wine_server_add_data( req, objattr, len );
|
|
ret = wine_server_call( req );
|
|
*handle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
RtlFreeHeap( GetProcessHeap(), 0, objattr );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtOpenKeyedEvent (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
if ((ret = validate_open_object_attributes( attr ))) return ret;
|
|
|
|
SERVER_START_REQ( open_keyed_event )
|
|
{
|
|
req->access = access;
|
|
req->attributes = attr->Attributes;
|
|
req->rootdir = wine_server_obj_handle( attr->RootDirectory );
|
|
if (attr->ObjectName)
|
|
wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
|
|
ret = wine_server_call( req );
|
|
*handle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtWaitForKeyedEvent (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key,
|
|
BOOLEAN alertable, const LARGE_INTEGER *timeout )
|
|
{
|
|
select_op_t select_op;
|
|
UINT flags = SELECT_INTERRUPTIBLE;
|
|
|
|
if (!handle) handle = keyed_event;
|
|
if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1;
|
|
if (alertable) flags |= SELECT_ALERTABLE;
|
|
select_op.keyed_event.op = SELECT_KEYED_EVENT_WAIT;
|
|
select_op.keyed_event.handle = wine_server_obj_handle( handle );
|
|
select_op.keyed_event.key = wine_server_client_ptr( key );
|
|
return server_select( &select_op, sizeof(select_op.keyed_event), flags, timeout );
|
|
}
|
|
|
|
/******************************************************************************
|
|
* NtReleaseKeyedEvent (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key,
|
|
BOOLEAN alertable, const LARGE_INTEGER *timeout )
|
|
{
|
|
select_op_t select_op;
|
|
UINT flags = SELECT_INTERRUPTIBLE;
|
|
|
|
if (!handle) handle = keyed_event;
|
|
if ((ULONG_PTR)key & 1) return STATUS_INVALID_PARAMETER_1;
|
|
if (alertable) flags |= SELECT_ALERTABLE;
|
|
select_op.keyed_event.op = SELECT_KEYED_EVENT_RELEASE;
|
|
select_op.keyed_event.handle = wine_server_obj_handle( handle );
|
|
select_op.keyed_event.key = wine_server_client_ptr( key );
|
|
return server_select( &select_op, sizeof(select_op.keyed_event), flags, timeout );
|
|
}
|
|
|
|
/******************************************************************
|
|
* NtCreateIoCompletion (NTDLL.@)
|
|
* ZwCreateIoCompletion (NTDLL.@)
|
|
*
|
|
* Creates I/O completion object.
|
|
*
|
|
* PARAMS
|
|
* CompletionPort [O] created completion object handle will be placed there
|
|
* DesiredAccess [I] desired access to a handle (combination of IO_COMPLETION_*)
|
|
* ObjectAttributes [I] completion object attributes
|
|
* NumberOfConcurrentThreads [I] desired number of concurrent active worker threads
|
|
*
|
|
*/
|
|
NTSTATUS WINAPI NtCreateIoCompletion( PHANDLE CompletionPort, ACCESS_MASK DesiredAccess,
|
|
POBJECT_ATTRIBUTES attr, ULONG NumberOfConcurrentThreads )
|
|
{
|
|
NTSTATUS status;
|
|
data_size_t len;
|
|
struct object_attributes *objattr;
|
|
|
|
TRACE("(%p, %x, %p, %d)\n", CompletionPort, DesiredAccess, attr, NumberOfConcurrentThreads);
|
|
|
|
if (!CompletionPort)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if ((status = alloc_object_attributes( attr, &objattr, &len ))) return status;
|
|
|
|
SERVER_START_REQ( create_completion )
|
|
{
|
|
req->access = DesiredAccess;
|
|
req->concurrent = NumberOfConcurrentThreads;
|
|
wine_server_add_data( req, objattr, len );
|
|
if (!(status = wine_server_call( req )))
|
|
*CompletionPort = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
RtlFreeHeap( GetProcessHeap(), 0, objattr );
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************
|
|
* NtSetIoCompletion (NTDLL.@)
|
|
* ZwSetIoCompletion (NTDLL.@)
|
|
*
|
|
* Inserts completion message into queue
|
|
*
|
|
* PARAMS
|
|
* CompletionPort [I] HANDLE to completion object
|
|
* CompletionKey [I] completion key
|
|
* CompletionValue [I] completion value (usually pointer to OVERLAPPED)
|
|
* Status [I] operation status
|
|
* NumberOfBytesTransferred [I] number of bytes transferred
|
|
*/
|
|
NTSTATUS WINAPI NtSetIoCompletion( HANDLE CompletionPort, ULONG_PTR CompletionKey,
|
|
ULONG_PTR CompletionValue, NTSTATUS Status,
|
|
SIZE_T NumberOfBytesTransferred )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
TRACE("(%p, %lx, %lx, %x, %lx)\n", CompletionPort, CompletionKey,
|
|
CompletionValue, Status, NumberOfBytesTransferred);
|
|
|
|
SERVER_START_REQ( add_completion )
|
|
{
|
|
req->handle = wine_server_obj_handle( CompletionPort );
|
|
req->ckey = CompletionKey;
|
|
req->cvalue = CompletionValue;
|
|
req->status = Status;
|
|
req->information = NumberOfBytesTransferred;
|
|
status = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************
|
|
* NtRemoveIoCompletion (NTDLL.@)
|
|
* ZwRemoveIoCompletion (NTDLL.@)
|
|
*
|
|
* (Wait for and) retrieve first completion message from completion object's queue
|
|
*
|
|
* PARAMS
|
|
* CompletionPort [I] HANDLE to I/O completion object
|
|
* CompletionKey [O] completion key
|
|
* CompletionValue [O] Completion value given in NtSetIoCompletion or in async operation
|
|
* iosb [O] IO_STATUS_BLOCK of completed asynchronous operation
|
|
* WaitTime [I] optional wait time in NTDLL format
|
|
*
|
|
*/
|
|
NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE CompletionPort, PULONG_PTR CompletionKey,
|
|
PULONG_PTR CompletionValue, PIO_STATUS_BLOCK iosb,
|
|
PLARGE_INTEGER WaitTime )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
TRACE("(%p, %p, %p, %p, %p)\n", CompletionPort, CompletionKey,
|
|
CompletionValue, iosb, WaitTime);
|
|
|
|
for(;;)
|
|
{
|
|
SERVER_START_REQ( remove_completion )
|
|
{
|
|
req->handle = wine_server_obj_handle( CompletionPort );
|
|
if (!(status = wine_server_call( req )))
|
|
{
|
|
*CompletionKey = reply->ckey;
|
|
*CompletionValue = reply->cvalue;
|
|
iosb->Information = reply->information;
|
|
iosb->u.Status = reply->status;
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
if (status != STATUS_PENDING) break;
|
|
|
|
status = NtWaitForSingleObject( CompletionPort, FALSE, WaitTime );
|
|
if (status != WAIT_OBJECT_0) break;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************
|
|
* NtRemoveIoCompletionEx (NTDLL.@)
|
|
* ZwRemoveIoCompletionEx (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE port, FILE_IO_COMPLETION_INFORMATION *info, ULONG count,
|
|
ULONG *written, LARGE_INTEGER *timeout, BOOLEAN alertable )
|
|
{
|
|
NTSTATUS ret;
|
|
ULONG i = 0;
|
|
|
|
TRACE("%p %p %u %p %p %u\n", port, info, count, written, timeout, alertable);
|
|
|
|
for (;;)
|
|
{
|
|
while (i < count)
|
|
{
|
|
SERVER_START_REQ( remove_completion )
|
|
{
|
|
req->handle = wine_server_obj_handle( port );
|
|
if (!(ret = wine_server_call( req )))
|
|
{
|
|
info[i].CompletionKey = reply->ckey;
|
|
info[i].CompletionValue = reply->cvalue;
|
|
info[i].IoStatusBlock.Information = reply->information;
|
|
info[i].IoStatusBlock.u.Status = reply->status;
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
if (ret != STATUS_SUCCESS) break;
|
|
|
|
++i;
|
|
}
|
|
|
|
if (i || ret != STATUS_PENDING)
|
|
{
|
|
if (ret == STATUS_PENDING)
|
|
ret = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
ret = NtWaitForSingleObject( port, alertable, timeout );
|
|
if (ret != WAIT_OBJECT_0) break;
|
|
}
|
|
|
|
*written = i ? i : 1;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************
|
|
* NtOpenIoCompletion (NTDLL.@)
|
|
* ZwOpenIoCompletion (NTDLL.@)
|
|
*
|
|
* Opens I/O completion object
|
|
*
|
|
* PARAMS
|
|
* CompletionPort [O] completion object handle will be placed there
|
|
* DesiredAccess [I] desired access to a handle (combination of IO_COMPLETION_*)
|
|
* ObjectAttributes [I] completion object name
|
|
*
|
|
*/
|
|
NTSTATUS WINAPI NtOpenIoCompletion( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
if (!handle) return STATUS_INVALID_PARAMETER;
|
|
if ((status = validate_open_object_attributes( attr ))) return status;
|
|
|
|
SERVER_START_REQ( open_completion )
|
|
{
|
|
req->access = access;
|
|
req->attributes = attr->Attributes;
|
|
req->rootdir = wine_server_obj_handle( attr->RootDirectory );
|
|
if (attr->ObjectName)
|
|
wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
|
|
status = wine_server_call( req );
|
|
*handle = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************
|
|
* NtQueryIoCompletion (NTDLL.@)
|
|
* ZwQueryIoCompletion (NTDLL.@)
|
|
*
|
|
* Requests information about given I/O completion object
|
|
*
|
|
* PARAMS
|
|
* CompletionPort [I] HANDLE to completion port to request
|
|
* InformationClass [I] information class
|
|
* CompletionInformation [O] user-provided buffer for data
|
|
* BufferLength [I] buffer length
|
|
* RequiredLength [O] required buffer length
|
|
*
|
|
*/
|
|
NTSTATUS WINAPI NtQueryIoCompletion( HANDLE CompletionPort, IO_COMPLETION_INFORMATION_CLASS InformationClass,
|
|
PVOID CompletionInformation, ULONG BufferLength, PULONG RequiredLength )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
TRACE("(%p, %d, %p, 0x%x, %p)\n", CompletionPort, InformationClass, CompletionInformation,
|
|
BufferLength, RequiredLength);
|
|
|
|
if (!CompletionInformation) return STATUS_INVALID_PARAMETER;
|
|
switch( InformationClass )
|
|
{
|
|
case IoCompletionBasicInformation:
|
|
{
|
|
ULONG *info = CompletionInformation;
|
|
|
|
if (RequiredLength) *RequiredLength = sizeof(*info);
|
|
if (BufferLength != sizeof(*info))
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
else
|
|
{
|
|
SERVER_START_REQ( query_completion )
|
|
{
|
|
req->handle = wine_server_obj_handle( CompletionPort );
|
|
if (!(status = wine_server_call( req )))
|
|
*info = reply->depth;
|
|
}
|
|
SERVER_END_REQ;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS NTDLL_AddCompletion( HANDLE hFile, ULONG_PTR CompletionValue,
|
|
NTSTATUS CompletionStatus, ULONG Information, BOOL async )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
SERVER_START_REQ( add_fd_completion )
|
|
{
|
|
req->handle = wine_server_obj_handle( hFile );
|
|
req->cvalue = CompletionValue;
|
|
req->status = CompletionStatus;
|
|
req->information = Information;
|
|
req->async = async;
|
|
status = wine_server_call( req );
|
|
}
|
|
SERVER_END_REQ;
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************
|
|
* RtlRunOnceInitialize (NTDLL.@)
|
|
*/
|
|
void WINAPI RtlRunOnceInitialize( RTL_RUN_ONCE *once )
|
|
{
|
|
once->Ptr = NULL;
|
|
}
|
|
|
|
/******************************************************************
|
|
* RtlRunOnceBeginInitialize (NTDLL.@)
|
|
*/
|
|
DWORD WINAPI RtlRunOnceBeginInitialize( RTL_RUN_ONCE *once, ULONG flags, void **context )
|
|
{
|
|
if (flags & RTL_RUN_ONCE_CHECK_ONLY)
|
|
{
|
|
ULONG_PTR val = (ULONG_PTR)once->Ptr;
|
|
|
|
if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
|
|
if ((val & 3) != 2) return STATUS_UNSUCCESSFUL;
|
|
if (context) *context = (void *)(val & ~3);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
ULONG_PTR next, val = (ULONG_PTR)once->Ptr;
|
|
|
|
switch (val & 3)
|
|
{
|
|
case 0: /* first time */
|
|
if (!interlocked_cmpxchg_ptr( &once->Ptr,
|
|
(flags & RTL_RUN_ONCE_ASYNC) ? (void *)3 : (void *)1, 0 ))
|
|
return STATUS_PENDING;
|
|
break;
|
|
|
|
case 1: /* in progress, wait */
|
|
if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
|
|
next = val & ~3;
|
|
if (interlocked_cmpxchg_ptr( &once->Ptr, (void *)((ULONG_PTR)&next | 1),
|
|
(void *)val ) == (void *)val)
|
|
NtWaitForKeyedEvent( 0, &next, FALSE, NULL );
|
|
break;
|
|
|
|
case 2: /* done */
|
|
if (context) *context = (void *)(val & ~3);
|
|
return STATUS_SUCCESS;
|
|
|
|
case 3: /* in progress, async */
|
|
if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************
|
|
* RtlRunOnceComplete (NTDLL.@)
|
|
*/
|
|
DWORD WINAPI RtlRunOnceComplete( RTL_RUN_ONCE *once, ULONG flags, void *context )
|
|
{
|
|
if ((ULONG_PTR)context & 3) return STATUS_INVALID_PARAMETER;
|
|
|
|
if (flags & RTL_RUN_ONCE_INIT_FAILED)
|
|
{
|
|
if (context) return STATUS_INVALID_PARAMETER;
|
|
if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
|
|
}
|
|
else context = (void *)((ULONG_PTR)context | 2);
|
|
|
|
for (;;)
|
|
{
|
|
ULONG_PTR val = (ULONG_PTR)once->Ptr;
|
|
|
|
switch (val & 3)
|
|
{
|
|
case 1: /* in progress */
|
|
if (interlocked_cmpxchg_ptr( &once->Ptr, context, (void *)val ) != (void *)val) break;
|
|
val &= ~3;
|
|
while (val)
|
|
{
|
|
ULONG_PTR next = *(ULONG_PTR *)val;
|
|
NtReleaseKeyedEvent( 0, (void *)val, FALSE, NULL );
|
|
val = next;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
|
|
case 3: /* in progress, async */
|
|
if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
|
|
if (interlocked_cmpxchg_ptr( &once->Ptr, context, (void *)val ) != (void *)val) break;
|
|
return STATUS_SUCCESS;
|
|
|
|
default:
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************
|
|
* RtlRunOnceExecuteOnce (NTDLL.@)
|
|
*/
|
|
DWORD WINAPI RtlRunOnceExecuteOnce( RTL_RUN_ONCE *once, PRTL_RUN_ONCE_INIT_FN func,
|
|
void *param, void **context )
|
|
{
|
|
DWORD ret = RtlRunOnceBeginInitialize( once, 0, context );
|
|
|
|
if (ret != STATUS_PENDING) return ret;
|
|
|
|
if (!func( once, param, context ))
|
|
{
|
|
RtlRunOnceComplete( once, RTL_RUN_ONCE_INIT_FAILED, NULL );
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return RtlRunOnceComplete( once, 0, context ? *context : NULL );
|
|
}
|
|
|
|
#ifdef __linux__
|
|
|
|
/* Futex-based SRW lock implementation:
|
|
*
|
|
* Since we can rely on the kernel to release all threads and don't need to
|
|
* worry about NtReleaseKeyedEvent(), we can simplify the layout a bit. The
|
|
* layout looks like this:
|
|
*
|
|
* 31 - Exclusive lock bit, set if the resource is owned exclusively.
|
|
* 30-16 - Number of exclusive waiters. Unlike the fallback implementation,
|
|
* this does not include the thread owning the lock, or shared threads
|
|
* waiting on the lock.
|
|
* 15 - Does this lock have any shared waiters? We use this as an
|
|
* optimization to avoid unnecessary FUTEX_WAKE_BITSET calls when
|
|
* releasing an exclusive lock.
|
|
* 14-0 - Number of shared owners. Unlike the fallback implementation, this
|
|
* does not include the number of shared threads waiting on the lock.
|
|
* Thus the state [1, x, >=1] will never occur.
|
|
*/
|
|
|
|
#define SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT 0x80000000
|
|
#define SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK 0x7fff0000
|
|
#define SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_INC 0x00010000
|
|
#define SRWLOCK_FUTEX_SHARED_WAITERS_BIT 0x00008000
|
|
#define SRWLOCK_FUTEX_SHARED_OWNERS_MASK 0x00007fff
|
|
#define SRWLOCK_FUTEX_SHARED_OWNERS_INC 0x00000001
|
|
|
|
/* Futex bitmasks; these are independent from the bits in the lock itself. */
|
|
#define SRWLOCK_FUTEX_BITSET_EXCLUSIVE 1
|
|
#define SRWLOCK_FUTEX_BITSET_SHARED 2
|
|
|
|
static NTSTATUS fast_try_acquire_srw_exclusive( RTL_SRWLOCK *lock )
|
|
{
|
|
int old, new;
|
|
NTSTATUS ret;
|
|
|
|
if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
|
|
|
|
do
|
|
{
|
|
old = *(int *)lock;
|
|
|
|
if (!(old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT)
|
|
&& !(old & SRWLOCK_FUTEX_SHARED_OWNERS_MASK))
|
|
{
|
|
/* Not locked exclusive or shared. We can try to grab it. */
|
|
new = old | SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT;
|
|
ret = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
new = old;
|
|
ret = STATUS_TIMEOUT;
|
|
}
|
|
} while (interlocked_cmpxchg( (int *)lock, new, old ) != old);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS fast_acquire_srw_exclusive( RTL_SRWLOCK *lock )
|
|
{
|
|
int old, new;
|
|
BOOLEAN wait;
|
|
|
|
if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
|
|
|
|
/* Atomically increment the exclusive waiter count. */
|
|
do
|
|
{
|
|
old = *(int *)lock;
|
|
new = old + SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_INC;
|
|
assert(new & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK);
|
|
} while (interlocked_cmpxchg( (int *)lock, new, old ) != old);
|
|
|
|
for (;;)
|
|
{
|
|
do
|
|
{
|
|
old = *(int *)lock;
|
|
|
|
if (!(old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT)
|
|
&& !(old & SRWLOCK_FUTEX_SHARED_OWNERS_MASK))
|
|
{
|
|
/* Not locked exclusive or shared. We can try to grab it. */
|
|
new = old | SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT;
|
|
assert(old & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK);
|
|
new -= SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_INC;
|
|
wait = FALSE;
|
|
}
|
|
else
|
|
{
|
|
new = old;
|
|
wait = TRUE;
|
|
}
|
|
} while (interlocked_cmpxchg( (int *)lock, new, old ) != old);
|
|
|
|
if (!wait)
|
|
return STATUS_SUCCESS;
|
|
|
|
futex_wait_bitset( (int *)lock, new, NULL, SRWLOCK_FUTEX_BITSET_EXCLUSIVE );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS fast_try_acquire_srw_shared( RTL_SRWLOCK *lock )
|
|
{
|
|
int new, old;
|
|
NTSTATUS ret;
|
|
|
|
if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
|
|
|
|
do
|
|
{
|
|
old = *(int *)lock;
|
|
|
|
if (!(old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT)
|
|
&& !(old & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK))
|
|
{
|
|
/* Not locked exclusive, and no exclusive waiters. We can try to
|
|
* grab it. */
|
|
new = old + SRWLOCK_FUTEX_SHARED_OWNERS_INC;
|
|
assert(new & SRWLOCK_FUTEX_SHARED_OWNERS_MASK);
|
|
ret = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
new = old;
|
|
ret = STATUS_TIMEOUT;
|
|
}
|
|
} while (interlocked_cmpxchg( (int *)lock, new, old ) != old);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static NTSTATUS fast_acquire_srw_shared( RTL_SRWLOCK *lock )
|
|
{
|
|
int old, new;
|
|
BOOLEAN wait;
|
|
|
|
if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
|
|
|
|
for (;;)
|
|
{
|
|
do
|
|
{
|
|
old = *(int *)lock;
|
|
|
|
if (!(old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT)
|
|
&& !(old & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK))
|
|
{
|
|
/* Not locked exclusive, and no exclusive waiters. We can try
|
|
* to grab it. */
|
|
new = old + SRWLOCK_FUTEX_SHARED_OWNERS_INC;
|
|
assert(new & SRWLOCK_FUTEX_SHARED_OWNERS_MASK);
|
|
wait = FALSE;
|
|
}
|
|
else
|
|
{
|
|
new = old | SRWLOCK_FUTEX_SHARED_WAITERS_BIT;
|
|
wait = TRUE;
|
|
}
|
|
} while (interlocked_cmpxchg( (int *)lock, new, old ) != old);
|
|
|
|
if (!wait)
|
|
return STATUS_SUCCESS;
|
|
|
|
futex_wait_bitset( (int *)lock, new, NULL, SRWLOCK_FUTEX_BITSET_SHARED );
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS fast_release_srw_exclusive( RTL_SRWLOCK *lock )
|
|
{
|
|
int old, new;
|
|
|
|
if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
|
|
|
|
do
|
|
{
|
|
old = *(int *)lock;
|
|
|
|
if (!(old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT))
|
|
{
|
|
ERR("Lock %p is not owned exclusive! (%#x)\n", lock, *(int *)lock);
|
|
return STATUS_RESOURCE_NOT_OWNED;
|
|
}
|
|
|
|
new = old & ~SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT;
|
|
|
|
if (!(new & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK))
|
|
new &= ~SRWLOCK_FUTEX_SHARED_WAITERS_BIT;
|
|
} while (interlocked_cmpxchg( (int *)lock, new, old ) != old);
|
|
|
|
if (new & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK)
|
|
futex_wake_bitset( (int *)lock, 1, SRWLOCK_FUTEX_BITSET_EXCLUSIVE );
|
|
else if (old & SRWLOCK_FUTEX_SHARED_WAITERS_BIT)
|
|
futex_wake_bitset( (int *)lock, INT_MAX, SRWLOCK_FUTEX_BITSET_SHARED );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS fast_release_srw_shared( RTL_SRWLOCK *lock )
|
|
{
|
|
int old, new;
|
|
|
|
if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
|
|
|
|
do
|
|
{
|
|
old = *(int *)lock;
|
|
|
|
if (old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT)
|
|
{
|
|
ERR("Lock %p is owned exclusive! (%#x)\n", lock, *(int *)lock);
|
|
return STATUS_RESOURCE_NOT_OWNED;
|
|
}
|
|
else if (!(old & SRWLOCK_FUTEX_SHARED_OWNERS_MASK))
|
|
{
|
|
ERR("Lock %p is not owned shared! (%#x)\n", lock, *(int *)lock);
|
|
return STATUS_RESOURCE_NOT_OWNED;
|
|
}
|
|
|
|
new = old - SRWLOCK_FUTEX_SHARED_OWNERS_INC;
|
|
} while (interlocked_cmpxchg( (int *)lock, new, old ) != old);
|
|
|
|
/* Optimization: only bother waking if there are actually exclusive waiters. */
|
|
if (!(new & SRWLOCK_FUTEX_SHARED_OWNERS_MASK) && (new & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK))
|
|
futex_wake_bitset( (int *)lock, 1, SRWLOCK_FUTEX_BITSET_EXCLUSIVE );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#else
|
|
|
|
static NTSTATUS fast_try_acquire_srw_exclusive( RTL_SRWLOCK *lock )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS fast_acquire_srw_exclusive( RTL_SRWLOCK *lock )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS fast_try_acquire_srw_shared( RTL_SRWLOCK *lock )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS fast_acquire_srw_shared( RTL_SRWLOCK *lock )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS fast_release_srw_exclusive( RTL_SRWLOCK *lock )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS fast_release_srw_shared( RTL_SRWLOCK *lock )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* SRW locks implementation
|
|
*
|
|
* The memory layout used by the lock is:
|
|
*
|
|
* 32 31 16 0
|
|
* ________________ ________________
|
|
* | X| #exclusive | #shared |
|
|
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
|
* Since there is no space left for a separate counter of shared access
|
|
* threads inside the locked section the #shared field is used for multiple
|
|
* purposes. The following table lists all possible states the lock can be
|
|
* in, notation: [X, #exclusive, #shared]:
|
|
*
|
|
* [0, 0, N] -> locked by N shared access threads, if N=0 it's unlocked
|
|
* [0, >=1, >=1] -> threads are requesting exclusive locks, but there are
|
|
* still shared access threads inside. #shared should not be incremented
|
|
* anymore!
|
|
* [1, >=1, >=0] -> lock is owned by an exclusive thread and the #shared
|
|
* counter can be used again to count the number of threads waiting in the
|
|
* queue for shared access.
|
|
*
|
|
* the following states are invalid and will never occur:
|
|
* [0, >=1, 0], [1, 0, >=0]
|
|
*
|
|
* The main problem arising from the fact that we have no separate counter
|
|
* of shared access threads inside the locked section is that in the state
|
|
* [0, >=1, >=1] above we cannot add additional waiting threads to the
|
|
* shared access queue - it wouldn't be possible to distinguish waiting
|
|
* threads and those that are still inside. To solve this problem the lock
|
|
* uses the following approach: a thread that isn't able to allocate a
|
|
* shared lock just uses the exclusive queue instead. As soon as the thread
|
|
* is woken up it is in the state [1, >=1, >=0]. In this state it's again
|
|
* possible to use the shared access queue. The thread atomically moves
|
|
* itself to the shared access queue and releases the exclusive lock, so
|
|
* that the "real" exclusive access threads have a chance. As soon as they
|
|
* are all ready the shared access threads are processed.
|
|
*/
|
|
|
|
#define SRWLOCK_MASK_IN_EXCLUSIVE 0x80000000
|
|
#define SRWLOCK_MASK_EXCLUSIVE_QUEUE 0x7fff0000
|
|
#define SRWLOCK_MASK_SHARED_QUEUE 0x0000ffff
|
|
#define SRWLOCK_RES_EXCLUSIVE 0x00010000
|
|
#define SRWLOCK_RES_SHARED 0x00000001
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
#define srwlock_key_exclusive(lock) (&lock->Ptr)
|
|
#define srwlock_key_shared(lock) ((void *)((char *)&lock->Ptr + 2))
|
|
#else
|
|
#define srwlock_key_exclusive(lock) ((void *)((char *)&lock->Ptr + 2))
|
|
#define srwlock_key_shared(lock) (&lock->Ptr)
|
|
#endif
|
|
|
|
static inline void srwlock_check_invalid( unsigned int val )
|
|
{
|
|
/* Throw exception if it's impossible to acquire/release this lock. */
|
|
if ((val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) == SRWLOCK_MASK_EXCLUSIVE_QUEUE ||
|
|
(val & SRWLOCK_MASK_SHARED_QUEUE) == SRWLOCK_MASK_SHARED_QUEUE)
|
|
RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED);
|
|
}
|
|
|
|
static inline unsigned int srwlock_lock_exclusive( unsigned int *dest, int incr )
|
|
{
|
|
unsigned int val, tmp;
|
|
/* Atomically modifies the value of *dest by adding incr. If the shared
|
|
* queue is empty and there are threads waiting for exclusive access, then
|
|
* sets the mark SRWLOCK_MASK_IN_EXCLUSIVE to signal other threads that
|
|
* they are allowed again to use the shared queue counter. */
|
|
for (val = *dest;; val = tmp)
|
|
{
|
|
tmp = val + incr;
|
|
srwlock_check_invalid( tmp );
|
|
if ((tmp & SRWLOCK_MASK_EXCLUSIVE_QUEUE) && !(tmp & SRWLOCK_MASK_SHARED_QUEUE))
|
|
tmp |= SRWLOCK_MASK_IN_EXCLUSIVE;
|
|
if ((tmp = interlocked_cmpxchg( (int *)dest, tmp, val )) == val)
|
|
break;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static inline unsigned int srwlock_unlock_exclusive( unsigned int *dest, int incr )
|
|
{
|
|
unsigned int val, tmp;
|
|
/* Atomically modifies the value of *dest by adding incr. If the queue of
|
|
* threads waiting for exclusive access is empty, then remove the
|
|
* SRWLOCK_MASK_IN_EXCLUSIVE flag (only the shared queue counter will
|
|
* remain). */
|
|
for (val = *dest;; val = tmp)
|
|
{
|
|
tmp = val + incr;
|
|
srwlock_check_invalid( tmp );
|
|
if (!(tmp & SRWLOCK_MASK_EXCLUSIVE_QUEUE))
|
|
tmp &= SRWLOCK_MASK_SHARED_QUEUE;
|
|
if ((tmp = interlocked_cmpxchg( (int *)dest, tmp, val )) == val)
|
|
break;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static inline void srwlock_leave_exclusive( RTL_SRWLOCK *lock, unsigned int val )
|
|
{
|
|
/* Used when a thread leaves an exclusive section. If there are other
|
|
* exclusive access threads they are processed first, followed by
|
|
* the shared waiters. */
|
|
if (val & SRWLOCK_MASK_EXCLUSIVE_QUEUE)
|
|
NtReleaseKeyedEvent( 0, srwlock_key_exclusive(lock), FALSE, NULL );
|
|
else
|
|
{
|
|
val &= SRWLOCK_MASK_SHARED_QUEUE; /* remove SRWLOCK_MASK_IN_EXCLUSIVE */
|
|
while (val--)
|
|
NtReleaseKeyedEvent( 0, srwlock_key_shared(lock), FALSE, NULL );
|
|
}
|
|
}
|
|
|
|
static inline void srwlock_leave_shared( RTL_SRWLOCK *lock, unsigned int val )
|
|
{
|
|
/* Wake up one exclusive thread as soon as the last shared access thread
|
|
* has left. */
|
|
if ((val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) && !(val & SRWLOCK_MASK_SHARED_QUEUE))
|
|
NtReleaseKeyedEvent( 0, srwlock_key_exclusive(lock), FALSE, NULL );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlInitializeSRWLock (NTDLL.@)
|
|
*
|
|
* NOTES
|
|
* Please note that SRWLocks do not keep track of the owner of a lock.
|
|
* It doesn't make any difference which thread for example unlocks an
|
|
* SRWLock (see corresponding tests). This implementation uses two
|
|
* keyed events (one for the exclusive waiters and one for the shared
|
|
* waiters) and is limited to 2^15-1 waiting threads.
|
|
*/
|
|
void WINAPI RtlInitializeSRWLock( RTL_SRWLOCK *lock )
|
|
{
|
|
lock->Ptr = NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlAcquireSRWLockExclusive (NTDLL.@)
|
|
*
|
|
* NOTES
|
|
* Unlike RtlAcquireResourceExclusive this function doesn't allow
|
|
* nested calls from the same thread. "Upgrading" a shared access lock
|
|
* to an exclusive access lock also doesn't seem to be supported.
|
|
*/
|
|
void WINAPI RtlAcquireSRWLockExclusive( RTL_SRWLOCK *lock )
|
|
{
|
|
if (fast_acquire_srw_exclusive( lock ) != STATUS_NOT_IMPLEMENTED)
|
|
return;
|
|
|
|
if (srwlock_lock_exclusive( (unsigned int *)&lock->Ptr, SRWLOCK_RES_EXCLUSIVE ))
|
|
NtWaitForKeyedEvent( 0, srwlock_key_exclusive(lock), FALSE, NULL );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlAcquireSRWLockShared (NTDLL.@)
|
|
*
|
|
* NOTES
|
|
* Do not call this function recursively - it will only succeed when
|
|
* there are no threads waiting for an exclusive lock!
|
|
*/
|
|
void WINAPI RtlAcquireSRWLockShared( RTL_SRWLOCK *lock )
|
|
{
|
|
unsigned int val, tmp;
|
|
|
|
if (fast_acquire_srw_shared( lock ) != STATUS_NOT_IMPLEMENTED)
|
|
return;
|
|
|
|
/* Acquires a shared lock. If it's currently not possible to add elements to
|
|
* the shared queue, then request exclusive access instead. */
|
|
for (val = *(unsigned int *)&lock->Ptr;; val = tmp)
|
|
{
|
|
if ((val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) && !(val & SRWLOCK_MASK_IN_EXCLUSIVE))
|
|
tmp = val + SRWLOCK_RES_EXCLUSIVE;
|
|
else
|
|
tmp = val + SRWLOCK_RES_SHARED;
|
|
if ((tmp = interlocked_cmpxchg( (int *)&lock->Ptr, tmp, val )) == val)
|
|
break;
|
|
}
|
|
|
|
/* Drop exclusive access again and instead requeue for shared access. */
|
|
if ((val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) && !(val & SRWLOCK_MASK_IN_EXCLUSIVE))
|
|
{
|
|
NtWaitForKeyedEvent( 0, srwlock_key_exclusive(lock), FALSE, NULL );
|
|
val = srwlock_unlock_exclusive( (unsigned int *)&lock->Ptr, (SRWLOCK_RES_SHARED
|
|
- SRWLOCK_RES_EXCLUSIVE) ) - SRWLOCK_RES_EXCLUSIVE;
|
|
srwlock_leave_exclusive( lock, val );
|
|
}
|
|
|
|
if (val & SRWLOCK_MASK_EXCLUSIVE_QUEUE)
|
|
NtWaitForKeyedEvent( 0, srwlock_key_shared(lock), FALSE, NULL );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlReleaseSRWLockExclusive (NTDLL.@)
|
|
*/
|
|
void WINAPI RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock )
|
|
{
|
|
if (fast_release_srw_exclusive( lock ) != STATUS_NOT_IMPLEMENTED)
|
|
return;
|
|
|
|
srwlock_leave_exclusive( lock, srwlock_unlock_exclusive( (unsigned int *)&lock->Ptr,
|
|
- SRWLOCK_RES_EXCLUSIVE ) - SRWLOCK_RES_EXCLUSIVE );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlReleaseSRWLockShared (NTDLL.@)
|
|
*/
|
|
void WINAPI RtlReleaseSRWLockShared( RTL_SRWLOCK *lock )
|
|
{
|
|
if (fast_release_srw_shared( lock ) != STATUS_NOT_IMPLEMENTED)
|
|
return;
|
|
|
|
srwlock_leave_shared( lock, srwlock_lock_exclusive( (unsigned int *)&lock->Ptr,
|
|
- SRWLOCK_RES_SHARED ) - SRWLOCK_RES_SHARED );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlTryAcquireSRWLockExclusive (NTDLL.@)
|
|
*
|
|
* NOTES
|
|
* Similarly to AcquireSRWLockExclusive, recursive calls are not allowed
|
|
* and will fail with a FALSE return value.
|
|
*/
|
|
BOOLEAN WINAPI RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock )
|
|
{
|
|
NTSTATUS ret;
|
|
|
|
if ((ret = fast_try_acquire_srw_exclusive( lock )) != STATUS_NOT_IMPLEMENTED)
|
|
return (ret == STATUS_SUCCESS);
|
|
|
|
return interlocked_cmpxchg( (int *)&lock->Ptr, SRWLOCK_MASK_IN_EXCLUSIVE |
|
|
SRWLOCK_RES_EXCLUSIVE, 0 ) == 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlTryAcquireSRWLockShared (NTDLL.@)
|
|
*/
|
|
BOOLEAN WINAPI RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock )
|
|
{
|
|
unsigned int val, tmp;
|
|
NTSTATUS ret;
|
|
|
|
if ((ret = fast_try_acquire_srw_shared( lock )) != STATUS_NOT_IMPLEMENTED)
|
|
return (ret == STATUS_SUCCESS);
|
|
|
|
for (val = *(unsigned int *)&lock->Ptr;; val = tmp)
|
|
{
|
|
if (val & SRWLOCK_MASK_EXCLUSIVE_QUEUE)
|
|
return FALSE;
|
|
if ((tmp = interlocked_cmpxchg( (int *)&lock->Ptr, val + SRWLOCK_RES_SHARED, val )) == val)
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
static NTSTATUS fast_wait_cv( RTL_CONDITION_VARIABLE *variable, int val, const LARGE_INTEGER *timeout )
|
|
{
|
|
struct timespec timespec;
|
|
int ret;
|
|
|
|
if (!use_futexes())
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
if (timeout && timeout->QuadPart != TIMEOUT_INFINITE)
|
|
{
|
|
timespec_from_timeout( ×pec, timeout );
|
|
ret = futex_wait( (int *)&variable->Ptr, val, ×pec );
|
|
}
|
|
else
|
|
ret = futex_wait( (int *)&variable->Ptr, val, NULL );
|
|
|
|
if (ret == -1 && errno == ETIMEDOUT)
|
|
return STATUS_TIMEOUT;
|
|
return STATUS_WAIT_0;
|
|
}
|
|
|
|
static NTSTATUS fast_wake_cv( RTL_CONDITION_VARIABLE *variable, int count )
|
|
{
|
|
if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
|
|
|
|
futex_wake( (int *)&variable->Ptr, count );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#else
|
|
static NTSTATUS fast_wait_cv( RTL_CONDITION_VARIABLE *variable, int val, const LARGE_INTEGER *timeout )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static NTSTATUS fast_wake_cv( RTL_CONDITION_VARIABLE *variable, int count )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
|
|
/***********************************************************************
|
|
* RtlInitializeConditionVariable (NTDLL.@)
|
|
*
|
|
* Initializes the condition variable with NULL.
|
|
*
|
|
* PARAMS
|
|
* variable [O] condition variable
|
|
*
|
|
* RETURNS
|
|
* Nothing.
|
|
*/
|
|
void WINAPI RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE *variable )
|
|
{
|
|
variable->Ptr = NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlWakeConditionVariable (NTDLL.@)
|
|
*
|
|
* Wakes up one thread waiting on the condition variable.
|
|
*
|
|
* PARAMS
|
|
* variable [I/O] condition variable to wake up.
|
|
*
|
|
* RETURNS
|
|
* Nothing.
|
|
*
|
|
* NOTES
|
|
* The calling thread does not have to own any lock in order to call
|
|
* this function.
|
|
*/
|
|
void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable )
|
|
{
|
|
interlocked_xchg_add( (int *)&variable->Ptr, 1 );
|
|
if (fast_wake_cv( variable, 1 ) == STATUS_NOT_IMPLEMENTED)
|
|
RtlWakeAddressSingle( variable );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlWakeAllConditionVariable (NTDLL.@)
|
|
*
|
|
* See WakeConditionVariable, wakes up all waiting threads.
|
|
*/
|
|
void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable )
|
|
{
|
|
interlocked_xchg_add( (int *)&variable->Ptr, 1 );
|
|
if (fast_wake_cv( variable, INT_MAX ) == STATUS_NOT_IMPLEMENTED)
|
|
RtlWakeAddressAll( variable );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlSleepConditionVariableCS (NTDLL.@)
|
|
*
|
|
* Atomically releases the critical section and suspends the thread,
|
|
* waiting for a Wake(All)ConditionVariable event. Afterwards it enters
|
|
* the critical section again and returns.
|
|
*
|
|
* PARAMS
|
|
* variable [I/O] condition variable
|
|
* crit [I/O] critical section to leave temporarily
|
|
* timeout [I] timeout
|
|
*
|
|
* RETURNS
|
|
* see NtWaitForKeyedEvent for all possible return values.
|
|
*/
|
|
NTSTATUS WINAPI RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE *variable, RTL_CRITICAL_SECTION *crit,
|
|
const LARGE_INTEGER *timeout )
|
|
{
|
|
NTSTATUS status;
|
|
int val = *(int *)&variable->Ptr;
|
|
|
|
RtlLeaveCriticalSection( crit );
|
|
|
|
if ((status = fast_wait_cv( variable, val, timeout )) == STATUS_NOT_IMPLEMENTED)
|
|
status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
|
|
|
|
RtlEnterCriticalSection( crit );
|
|
|
|
return status;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlSleepConditionVariableSRW (NTDLL.@)
|
|
*
|
|
* Atomically releases the SRWLock and suspends the thread,
|
|
* waiting for a Wake(All)ConditionVariable event. Afterwards it enters
|
|
* the SRWLock again with the same access rights and returns.
|
|
*
|
|
* PARAMS
|
|
* variable [I/O] condition variable
|
|
* lock [I/O] SRWLock to leave temporarily
|
|
* timeout [I] timeout
|
|
* flags [I] type of the current lock (exclusive / shared)
|
|
*
|
|
* RETURNS
|
|
* see NtWaitForKeyedEvent for all possible return values.
|
|
*
|
|
* NOTES
|
|
* the behaviour is undefined if the thread doesn't own the lock.
|
|
*/
|
|
NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, RTL_SRWLOCK *lock,
|
|
const LARGE_INTEGER *timeout, ULONG flags )
|
|
{
|
|
NTSTATUS status;
|
|
int val = *(int *)&variable->Ptr;
|
|
|
|
if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
|
|
RtlReleaseSRWLockShared( lock );
|
|
else
|
|
RtlReleaseSRWLockExclusive( lock );
|
|
|
|
if ((status = fast_wait_cv( variable, val, timeout )) == STATUS_NOT_IMPLEMENTED)
|
|
status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
|
|
|
|
if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
|
|
RtlAcquireSRWLockShared( lock );
|
|
else
|
|
RtlAcquireSRWLockExclusive( lock );
|
|
return status;
|
|
}
|
|
|
|
static RTL_CRITICAL_SECTION addr_section;
|
|
static RTL_CRITICAL_SECTION_DEBUG addr_section_debug =
|
|
{
|
|
0, 0, &addr_section,
|
|
{ &addr_section_debug.ProcessLocksList, &addr_section_debug.ProcessLocksList },
|
|
0, 0, { (DWORD_PTR)(__FILE__ ": addr_section") }
|
|
};
|
|
static RTL_CRITICAL_SECTION addr_section = { &addr_section_debug, -1, 0, 0, 0, 0 };
|
|
|
|
static BOOL compare_addr( const void *addr, const void *cmp, SIZE_T size )
|
|
{
|
|
switch (size)
|
|
{
|
|
case 1:
|
|
return (*(const UCHAR *)addr == *(const UCHAR *)cmp);
|
|
case 2:
|
|
return (*(const USHORT *)addr == *(const USHORT *)cmp);
|
|
case 4:
|
|
return (*(const ULONG *)addr == *(const ULONG *)cmp);
|
|
case 8:
|
|
return (*(const ULONG64 *)addr == *(const ULONG64 *)cmp);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
/* We can't map addresses to futex directly, because an application can wait on
|
|
* 8 bytes, and we can't pass all 8 as the compare value to futex(). Instead we
|
|
* map all addresses to a small fixed table of futexes. This may result in
|
|
* spurious wakes, but the application is already expected to handle those. */
|
|
|
|
static int addr_futex_table[256];
|
|
|
|
static inline int *hash_addr( const void *addr )
|
|
{
|
|
ULONG_PTR val = (ULONG_PTR)addr;
|
|
|
|
return &addr_futex_table[(val >> 2) & 255];
|
|
}
|
|
|
|
static inline NTSTATUS fast_wait_addr( const void *addr, const void *cmp, SIZE_T size,
|
|
const LARGE_INTEGER *timeout )
|
|
{
|
|
int *futex;
|
|
int val;
|
|
struct timespec timespec;
|
|
int ret;
|
|
|
|
if (!use_futexes())
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
futex = hash_addr( addr );
|
|
|
|
/* We must read the previous value of the futex before checking the value
|
|
* of the address being waited on. That way, if we receive a wake between
|
|
* now and waiting on the futex, we know that val will have changed.
|
|
* Use an atomic load so that memory accesses are ordered between this read
|
|
* and the increment below. */
|
|
val = interlocked_cmpxchg( futex, 0, 0 );
|
|
if (!compare_addr( addr, cmp, size ))
|
|
return STATUS_SUCCESS;
|
|
|
|
if (timeout)
|
|
{
|
|
timespec_from_timeout( ×pec, timeout );
|
|
ret = futex_wait( futex, val, ×pec );
|
|
}
|
|
else
|
|
ret = futex_wait( futex, val, NULL );
|
|
|
|
if (ret == -1 && errno == ETIMEDOUT)
|
|
return STATUS_TIMEOUT;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static inline NTSTATUS fast_wake_addr( const void *addr )
|
|
{
|
|
int *futex;
|
|
|
|
if (!use_futexes())
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
|
|
futex = hash_addr( addr );
|
|
|
|
interlocked_xchg_add( futex, 1 );
|
|
|
|
futex_wake( futex, INT_MAX );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#else
|
|
static inline NTSTATUS fast_wait_addr( const void *addr, const void *cmp, SIZE_T size,
|
|
const LARGE_INTEGER *timeout )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static inline NTSTATUS fast_wake_addr( const void *addr )
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
|
|
/***********************************************************************
|
|
* RtlWaitOnAddress (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size,
|
|
const LARGE_INTEGER *timeout )
|
|
{
|
|
select_op_t select_op;
|
|
NTSTATUS ret;
|
|
int cookie;
|
|
BOOL user_apc = FALSE;
|
|
obj_handle_t apc_handle = 0;
|
|
apc_call_t call;
|
|
apc_result_t result;
|
|
timeout_t abs_timeout = timeout ? timeout->QuadPart : TIMEOUT_INFINITE;
|
|
sigset_t old_set;
|
|
|
|
if (size != 1 && size != 2 && size != 4 && size != 8)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if ((ret = fast_wait_addr( addr, cmp, size, timeout )) != STATUS_NOT_IMPLEMENTED)
|
|
return ret;
|
|
|
|
select_op.keyed_event.op = SELECT_KEYED_EVENT_WAIT;
|
|
select_op.keyed_event.handle = wine_server_obj_handle( keyed_event );
|
|
select_op.keyed_event.key = wine_server_client_ptr( addr );
|
|
|
|
memset( &result, 0, sizeof(result) );
|
|
|
|
do
|
|
{
|
|
RtlEnterCriticalSection( &addr_section );
|
|
if (!compare_addr( addr, cmp, size ))
|
|
{
|
|
RtlLeaveCriticalSection( &addr_section );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pthread_sigmask( SIG_BLOCK, &server_block_set, &old_set );
|
|
for (;;)
|
|
{
|
|
SERVER_START_REQ( select )
|
|
{
|
|
req->flags = SELECT_INTERRUPTIBLE;
|
|
req->cookie = wine_server_client_ptr( &cookie );
|
|
req->prev_apc = apc_handle;
|
|
req->timeout = abs_timeout;
|
|
wine_server_add_data( req, &result, sizeof(result) );
|
|
wine_server_add_data( req, &select_op, sizeof(select_op.keyed_event) );
|
|
ret = server_call_unlocked( req );
|
|
abs_timeout = reply->timeout;
|
|
apc_handle = reply->apc_handle;
|
|
call = reply->call;
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
if (ret != STATUS_KERNEL_APC) break;
|
|
invoke_apc( &call, &result );
|
|
}
|
|
pthread_sigmask( SIG_SETMASK, &old_set, NULL );
|
|
|
|
RtlLeaveCriticalSection( &addr_section );
|
|
|
|
if (ret == STATUS_USER_APC)
|
|
{
|
|
invoke_apc( &call, &result );
|
|
/* if we ran a user apc we have to check once more if additional apcs are queued,
|
|
* but we don't want to wait */
|
|
abs_timeout = 0;
|
|
user_apc = TRUE;
|
|
}
|
|
|
|
if (ret == STATUS_PENDING) ret = wait_select_reply( &cookie );
|
|
}
|
|
while (ret == STATUS_USER_APC || ret == STATUS_KERNEL_APC);
|
|
|
|
if (ret == STATUS_TIMEOUT && user_apc) ret = STATUS_USER_APC;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlWakeAddressAll (NTDLL.@)
|
|
*/
|
|
void WINAPI RtlWakeAddressAll( const void *addr )
|
|
{
|
|
if (fast_wake_addr( addr ) != STATUS_NOT_IMPLEMENTED)
|
|
return;
|
|
|
|
RtlEnterCriticalSection( &addr_section );
|
|
while (NtReleaseKeyedEvent( 0, addr, 0, &zero_timeout ) == STATUS_SUCCESS) {}
|
|
RtlLeaveCriticalSection( &addr_section );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RtlWakeAddressSingle (NTDLL.@)
|
|
*/
|
|
void WINAPI RtlWakeAddressSingle( const void *addr )
|
|
{
|
|
if (fast_wake_addr( addr ) != STATUS_NOT_IMPLEMENTED)
|
|
return;
|
|
|
|
RtlEnterCriticalSection( &addr_section );
|
|
NtReleaseKeyedEvent( 0, addr, 0, &zero_timeout );
|
|
RtlLeaveCriticalSection( &addr_section );
|
|
}
|