wine-wine/dlls/ntdll/sync.c

1539 lines
51 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);
/* 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( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
LONG initial, LONG max )
{
return unix_funcs->NtCreateSemaphore( handle, access, attr, initial, max );
}
/******************************************************************************
* NtOpenSemaphore (NTDLL.@)
*/
NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
{
return unix_funcs->NtOpenSemaphore( handle, access, attr );
}
/******************************************************************************
* NtQuerySemaphore (NTDLL.@)
*/
NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS class,
void *info, ULONG len, ULONG *ret_len )
{
return unix_funcs->NtQuerySemaphore( handle, class, info, len, ret_len );
}
/******************************************************************************
* NtReleaseSemaphore (NTDLL.@)
*/
NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous )
{
return unix_funcs->NtReleaseSemaphore( handle, count, previous );
}
/*
* Events
*/
/**************************************************************************
* NtCreateEvent (NTDLL.@)
* ZwCreateEvent (NTDLL.@)
*/
NTSTATUS WINAPI NtCreateEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
EVENT_TYPE type, BOOLEAN state )
{
return unix_funcs->NtCreateEvent( handle, access, attr, type, state );
}
/******************************************************************************
* NtOpenEvent (NTDLL.@)
* ZwOpenEvent (NTDLL.@)
*/
NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
{
return unix_funcs->NtOpenEvent( handle, access, attr );
}
/******************************************************************************
* NtSetEvent (NTDLL.@)
* ZwSetEvent (NTDLL.@)
*/
NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state )
{
return unix_funcs->NtSetEvent( handle, prev_state );
}
/******************************************************************************
* NtResetEvent (NTDLL.@)
*/
NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state )
{
return unix_funcs->NtResetEvent( handle, prev_state );
}
/******************************************************************************
* NtClearEvent (NTDLL.@)
*
* FIXME
* same as NtResetEvent ???
*/
NTSTATUS WINAPI NtClearEvent ( HANDLE handle )
{
return unix_funcs->NtClearEvent( handle );
}
/******************************************************************************
* NtPulseEvent (NTDLL.@)
*
* FIXME
* PulseCount
*/
NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state )
{
return unix_funcs->NtPulseEvent( handle, prev_state );
}
/******************************************************************************
* NtQueryEvent (NTDLL.@)
*/
NTSTATUS WINAPI NtQueryEvent( HANDLE handle, EVENT_INFORMATION_CLASS class,
void *info, ULONG len, ULONG *ret_len )
{
return unix_funcs->NtQueryEvent( handle, class, info, len, ret_len );
}
/*
* Mutants (known as Mutexes in Kernel32)
*/
/******************************************************************************
* NtCreateMutant [NTDLL.@]
* ZwCreateMutant [NTDLL.@]
*/
NTSTATUS WINAPI NtCreateMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
BOOLEAN owned )
{
return unix_funcs->NtCreateMutant( handle, access, attr, owned );
}
/**************************************************************************
* NtOpenMutant [NTDLL.@]
* ZwOpenMutant [NTDLL.@]
*/
NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
{
return unix_funcs->NtOpenMutant( handle, access, attr );
}
/**************************************************************************
* NtReleaseMutant [NTDLL.@]
* ZwReleaseMutant [NTDLL.@]
*/
NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count )
{
return unix_funcs->NtReleaseMutant( handle, prev_count );
}
/******************************************************************
* NtQueryMutant [NTDLL.@]
* ZwQueryMutant [NTDLL.@]
*/
NTSTATUS WINAPI NtQueryMutant( HANDLE handle, MUTANT_INFORMATION_CLASS class,
void *info, ULONG len, ULONG *ret_len )
{
return unix_funcs->NtQueryMutant( handle, class, info, len, ret_len );
}
/*
* 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 )
{
NTSTATUS ret;
TRACE( "semi-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;
SERVER_START_REQ(get_job_info)
{
req->handle = wine_server_obj_handle( handle );
if ((ret = wine_server_call( req )) == STATUS_SUCCESS)
{
memset(accounting, 0, sizeof(*accounting));
accounting->TotalProcesses = reply->total_processes;
accounting->ActiveProcesses = reply->active_processes;
}
}
SERVER_END_REQ;
if (ret_len) *ret_len = sizeof(*accounting);
return ret;
}
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( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
TIMER_TYPE type )
{
return unix_funcs->NtCreateTimer( handle, access, attr, type );
}
/**************************************************************************
* NtOpenTimer [NTDLL.@]
* ZwOpenTimer [NTDLL.@]
*/
NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
{
return unix_funcs->NtOpenTimer( handle, access, attr );
}
/**************************************************************************
* NtSetTimer [NTDLL.@]
* ZwSetTimer [NTDLL.@]
*/
NTSTATUS WINAPI NtSetTimer( HANDLE handle, const LARGE_INTEGER *when, PTIMER_APC_ROUTINE callback,
void *arg, BOOLEAN resume, ULONG period, BOOLEAN *state )
{
return unix_funcs->NtSetTimer( handle, when, callback, arg, resume, period, state );
}
/**************************************************************************
* NtCancelTimer [NTDLL.@]
* ZwCancelTimer [NTDLL.@]
*/
NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state )
{
return unix_funcs->NtCancelTimer( handle, state );
}
/******************************************************************************
* NtQueryTimer (NTDLL.@)
*/
NTSTATUS WINAPI NtQueryTimer( HANDLE handle, TIMER_INFORMATION_CLASS class,
void *info, ULONG len, ULONG *ret_len )
{
return unix_funcs->NtQueryTimer( handle, class, info, len, ret_len );
}
/******************************************************************************
* 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 */
/******************************************************************
* NtWaitForMultipleObjects (NTDLL.@)
*/
NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles,
BOOLEAN wait_any, BOOLEAN alertable,
const LARGE_INTEGER *timeout )
{
return unix_funcs->NtWaitForMultipleObjects( count, handles, wait_any, alertable, timeout );
}
/******************************************************************
* NtWaitForSingleObject (NTDLL.@)
*/
NTSTATUS WINAPI NtWaitForSingleObject(HANDLE handle, BOOLEAN alertable, const LARGE_INTEGER *timeout )
{
return unix_funcs->NtWaitForSingleObject( handle, alertable, timeout );
}
/******************************************************************
* NtSignalAndWaitForSingleObject (NTDLL.@)
*/
NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait,
BOOLEAN alertable, const LARGE_INTEGER *timeout )
{
return unix_funcs->NtSignalAndWaitForSingleObject( signal, wait, alertable, timeout );
}
/******************************************************************
* NtYieldExecution (NTDLL.@)
*/
NTSTATUS WINAPI NtYieldExecution(void)
{
return unix_funcs->NtYieldExecution();
}
/******************************************************************
* NtDelayExecution (NTDLL.@)
*/
NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeout )
{
return unix_funcs->NtDelayExecution( alertable, timeout );
}
/******************************************************************************
* NtCreateKeyedEvent (NTDLL.@)
*/
NTSTATUS WINAPI NtCreateKeyedEvent( HANDLE *handle, ACCESS_MASK access,
const OBJECT_ATTRIBUTES *attr, ULONG flags )
{
return unix_funcs->NtCreateKeyedEvent( handle, access, attr, flags );
}
/******************************************************************************
* NtOpenKeyedEvent (NTDLL.@)
*/
NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
{
return unix_funcs->NtOpenKeyedEvent( handle, access, attr );
}
/******************************************************************************
* NtWaitForKeyedEvent (NTDLL.@)
*/
NTSTATUS WINAPI NtWaitForKeyedEvent( HANDLE handle, const void *key,
BOOLEAN alertable, const LARGE_INTEGER *timeout )
{
return unix_funcs->NtWaitForKeyedEvent( handle, key, alertable, timeout );
}
/******************************************************************************
* NtReleaseKeyedEvent (NTDLL.@)
*/
NTSTATUS WINAPI NtReleaseKeyedEvent( HANDLE handle, const void *key,
BOOLEAN alertable, const LARGE_INTEGER *timeout )
{
return unix_funcs->NtReleaseKeyedEvent( handle, key, alertable, 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 (!InterlockedCompareExchangePointer( &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 (InterlockedCompareExchangePointer( &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 (InterlockedCompareExchangePointer( &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 (InterlockedCompareExchangePointer( &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 );
}
/* 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) ((void *)(((ULONG_PTR)&lock->Ptr + 1) & ~1))
#define srwlock_key_shared(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 3) & ~1))
#else
#define srwlock_key_exclusive(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 3) & ~1))
#define srwlock_key_shared(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 1) & ~1))
#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 = InterlockedCompareExchange( (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 = InterlockedCompareExchange( (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 (unix_funcs->fast_RtlAcquireSRWLockExclusive( 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 (unix_funcs->fast_RtlAcquireSRWLockShared( 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 = InterlockedCompareExchange( (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 (unix_funcs->fast_RtlReleaseSRWLockExclusive( 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 (unix_funcs->fast_RtlReleaseSRWLockShared( 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 = unix_funcs->fast_RtlTryAcquireSRWLockExclusive( lock )) != STATUS_NOT_IMPLEMENTED)
return (ret == STATUS_SUCCESS);
return InterlockedCompareExchange( (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 = unix_funcs->fast_RtlTryAcquireSRWLockShared( 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 = InterlockedCompareExchange( (int *)&lock->Ptr, val + SRWLOCK_RES_SHARED, val )) == val)
break;
}
return TRUE;
}
/***********************************************************************
* 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 )
{
if (unix_funcs->fast_RtlWakeConditionVariable( variable, 1 ) == STATUS_NOT_IMPLEMENTED)
{
InterlockedIncrement( (int *)&variable->Ptr );
RtlWakeAddressSingle( variable );
}
}
/***********************************************************************
* RtlWakeAllConditionVariable (NTDLL.@)
*
* See WakeConditionVariable, wakes up all waiting threads.
*/
void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable )
{
if (unix_funcs->fast_RtlWakeConditionVariable( variable, INT_MAX ) == STATUS_NOT_IMPLEMENTED)
{
InterlockedIncrement( (int *)&variable->Ptr );
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;
if ((status = unix_funcs->fast_RtlSleepConditionVariableCS( variable, crit,
timeout )) != STATUS_NOT_IMPLEMENTED)
return status;
val = *(int *)&variable->Ptr;
RtlLeaveCriticalSection( crit );
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;
if ((status = unix_funcs->fast_RtlSleepConditionVariableSRW( variable, lock, timeout,
flags )) != STATUS_NOT_IMPLEMENTED)
return status;
val = *(int *)&variable->Ptr;
if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
RtlReleaseSRWLockShared( lock );
else
RtlReleaseSRWLockExclusive( lock );
status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
RtlAcquireSRWLockShared( lock );
else
RtlAcquireSRWLockExclusive( lock );
return status;
}
/***********************************************************************
* RtlWaitOnAddress (NTDLL.@)
*/
NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size,
const LARGE_INTEGER *timeout )
{
return unix_funcs->RtlWaitOnAddress( addr, cmp, size, timeout );
}
/***********************************************************************
* RtlWakeAddressAll (NTDLL.@)
*/
void WINAPI RtlWakeAddressAll( const void *addr )
{
return unix_funcs->RtlWakeAddressAll( addr );
}
/***********************************************************************
* RtlWakeAddressSingle (NTDLL.@)
*/
void WINAPI RtlWakeAddressSingle( const void *addr )
{
return unix_funcs->RtlWakeAddressSingle( addr );
}