| /* | |
| * 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 <assert.h> | |
| #include <errno.h> | |
| #include <signal.h> | |
| #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> | |
| #define NONAMELESSUNION | |
| #define NONAMELESSSTRUCT | |
| #include "ntstatus.h" | |
| #define WIN32_NO_STATUS | |
| #include "windef.h" | |
| #include "winternl.h" | |
| #include "wine/server.h" | |
| #include "wine/debug.h" | |
| #include "ntdll_misc.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(ntdll); | |
| /* creates a struct security_descriptor and contained information in one contiguous piece of memory */ | |
| NTSTATUS NTDLL_create_struct_sd(PSECURITY_DESCRIPTOR nt_sd, struct security_descriptor **server_sd, | |
| data_size_t *server_sd_len) | |
| { | |
| unsigned int len; | |
| PSID owner, group; | |
| ACL *dacl, *sacl; | |
| BOOLEAN owner_present, group_present, dacl_present, sacl_present; | |
| BOOLEAN defaulted; | |
| NTSTATUS status; | |
| unsigned char *ptr; | |
| if (!nt_sd) | |
| { | |
| *server_sd = NULL; | |
| *server_sd_len = 0; | |
| return STATUS_SUCCESS; | |
| } | |
| len = sizeof(struct security_descriptor); | |
| status = RtlGetOwnerSecurityDescriptor(nt_sd, &owner, &owner_present); | |
| if (status != STATUS_SUCCESS) return status; | |
| status = RtlGetGroupSecurityDescriptor(nt_sd, &group, &group_present); | |
| if (status != STATUS_SUCCESS) return status; | |
| status = RtlGetSaclSecurityDescriptor(nt_sd, &sacl_present, &sacl, &defaulted); | |
| if (status != STATUS_SUCCESS) return status; | |
| status = RtlGetDaclSecurityDescriptor(nt_sd, &dacl_present, &dacl, &defaulted); | |
| if (status != STATUS_SUCCESS) return status; | |
| if (owner_present) | |
| len += RtlLengthSid(owner); | |
| if (group_present) | |
| 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); | |
| *server_sd = RtlAllocateHeap(GetProcessHeap(), 0, len); | |
| if (!*server_sd) return STATUS_NO_MEMORY; | |
| (*server_sd)->control = ((SECURITY_DESCRIPTOR *)nt_sd)->Control & ~SE_SELF_RELATIVE; | |
| (*server_sd)->owner_len = owner_present ? RtlLengthSid(owner) : 0; | |
| (*server_sd)->group_len = group_present ? RtlLengthSid(group) : 0; | |
| (*server_sd)->sacl_len = (sacl_present && sacl) ? sacl->AclSize : 0; | |
| (*server_sd)->dacl_len = (dacl_present && dacl) ? dacl->AclSize : 0; | |
| ptr = (unsigned char *)(*server_sd + 1); | |
| memcpy(ptr, owner, (*server_sd)->owner_len); | |
| ptr += (*server_sd)->owner_len; | |
| memcpy(ptr, group, (*server_sd)->group_len); | |
| ptr += (*server_sd)->group_len; | |
| memcpy(ptr, sacl, (*server_sd)->sacl_len); | |
| ptr += (*server_sd)->sacl_len; | |
| memcpy(ptr, dacl, (*server_sd)->dacl_len); | |
| *server_sd_len = len; | |
| return STATUS_SUCCESS; | |
| } | |
| /* frees a struct security_descriptor allocated by NTDLL_create_struct_sd */ | |
| void NTDLL_free_struct_sd(struct security_descriptor *server_sd) | |
| { | |
| RtlFreeHeap(GetProcessHeap(), 0, server_sd); | |
| } | |
| /* | |
| * 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 ) | |
| { | |
| DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; | |
| NTSTATUS ret; | |
| struct object_attributes objattr; | |
| struct security_descriptor *sd = NULL; | |
| if (MaximumCount <= 0 || InitialCount < 0 || InitialCount > MaximumCount) | |
| return STATUS_INVALID_PARAMETER; | |
| if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; | |
| objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); | |
| objattr.sd_len = 0; | |
| objattr.name_len = len; | |
| if (attr) | |
| { | |
| ret = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len ); | |
| if (ret != STATUS_SUCCESS) return ret; | |
| } | |
| SERVER_START_REQ( create_semaphore ) | |
| { | |
| req->access = access; | |
| req->attributes = (attr) ? attr->Attributes : 0; | |
| req->initial = InitialCount; | |
| req->max = MaximumCount; | |
| wine_server_add_data( req, &objattr, sizeof(objattr) ); | |
| if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len ); | |
| if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); | |
| ret = wine_server_call( req ); | |
| *SemaphoreHandle = wine_server_ptr_handle( reply->handle ); | |
| } | |
| SERVER_END_REQ; | |
| NTDLL_free_struct_sd( sd ); | |
| return ret; | |
| } | |
| /****************************************************************************** | |
| * NtOpenSemaphore (NTDLL.@) | |
| */ | |
| NTSTATUS WINAPI NtOpenSemaphore( OUT PHANDLE SemaphoreHandle, | |
| IN ACCESS_MASK access, | |
| IN const OBJECT_ATTRIBUTES *attr ) | |
| { | |
| DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; | |
| NTSTATUS ret; | |
| if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; | |
| SERVER_START_REQ( open_semaphore ) | |
| { | |
| req->access = access; | |
| req->attributes = (attr) ? attr->Attributes : 0; | |
| req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); | |
| if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); | |
| ret = wine_server_call( req ); | |
| *SemaphoreHandle = wine_server_ptr_handle( reply->handle ); | |
| } | |
| SERVER_END_REQ; | |
| return ret; | |
| } | |
| /****************************************************************************** | |
| * NtQuerySemaphore (NTDLL.@) | |
| */ | |
| NTSTATUS WINAPI NtQuerySemaphore( | |
| HANDLE SemaphoreHandle, | |
| SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass, | |
| PVOID SemaphoreInformation, | |
| ULONG Length, | |
| PULONG ReturnLength) | |
| { | |
| FIXME("(%p,%d,%p,0x%08x,%p) stub!\n", | |
| SemaphoreHandle, SemaphoreInformationClass, SemaphoreInformation, Length, ReturnLength); | |
| return STATUS_SUCCESS; | |
| } | |
| /****************************************************************************** | |
| * 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) | |
| { | |
| DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; | |
| NTSTATUS ret; | |
| struct security_descriptor *sd = NULL; | |
| struct object_attributes objattr; | |
| if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; | |
| objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); | |
| objattr.sd_len = 0; | |
| objattr.name_len = len; | |
| if (attr) | |
| { | |
| ret = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len ); | |
| if (ret != STATUS_SUCCESS) return ret; | |
| } | |
| SERVER_START_REQ( create_event ) | |
| { | |
| req->access = DesiredAccess; | |
| req->attributes = (attr) ? attr->Attributes : 0; | |
| req->manual_reset = (type == NotificationEvent); | |
| req->initial_state = InitialState; | |
| wine_server_add_data( req, &objattr, sizeof(objattr) ); | |
| if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len ); | |
| if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); | |
| ret = wine_server_call( req ); | |
| *EventHandle = wine_server_ptr_handle( reply->handle ); | |
| } | |
| SERVER_END_REQ; | |
| NTDLL_free_struct_sd( sd ); | |
| return ret; | |
| } | |
| /****************************************************************************** | |
| * NtOpenEvent (NTDLL.@) | |
| * ZwOpenEvent (NTDLL.@) | |
| */ | |
| NTSTATUS WINAPI NtOpenEvent( | |
| OUT PHANDLE EventHandle, | |
| IN ACCESS_MASK DesiredAccess, | |
| IN const OBJECT_ATTRIBUTES *attr ) | |
| { | |
| DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; | |
| NTSTATUS ret; | |
| if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; | |
| SERVER_START_REQ( open_event ) | |
| { | |
| req->access = DesiredAccess; | |
| req->attributes = (attr) ? attr->Attributes : 0; | |
| req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); | |
| if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); | |
| ret = wine_server_call( req ); | |
| *EventHandle = wine_server_ptr_handle( reply->handle ); | |
| } | |
| SERVER_END_REQ; | |
| return ret; | |
| } | |
| /****************************************************************************** | |
| * NtSetEvent (NTDLL.@) | |
| * ZwSetEvent (NTDLL.@) | |
| */ | |
| NTSTATUS WINAPI NtSetEvent( HANDLE handle, PULONG NumberOfThreadsReleased ) | |
| { | |
| NTSTATUS ret; | |
| /* FIXME: set NumberOfThreadsReleased */ | |
| SERVER_START_REQ( event_op ) | |
| { | |
| req->handle = wine_server_obj_handle( handle ); | |
| req->op = SET_EVENT; | |
| ret = wine_server_call( req ); | |
| } | |
| SERVER_END_REQ; | |
| return ret; | |
| } | |
| /****************************************************************************** | |
| * NtResetEvent (NTDLL.@) | |
| */ | |
| NTSTATUS WINAPI NtResetEvent( HANDLE handle, PULONG NumberOfThreadsReleased ) | |
| { | |
| NTSTATUS ret; | |
| /* resetting an event can't release any thread... */ | |
| if (NumberOfThreadsReleased) *NumberOfThreadsReleased = 0; | |
| SERVER_START_REQ( event_op ) | |
| { | |
| req->handle = wine_server_obj_handle( handle ); | |
| req->op = RESET_EVENT; | |
| ret = wine_server_call( req ); | |
| } | |
| 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, PULONG PulseCount ) | |
| { | |
| NTSTATUS ret; | |
| if (PulseCount) | |
| FIXME("(%p,%d)\n", handle, *PulseCount); | |
| SERVER_START_REQ( event_op ) | |
| { | |
| req->handle = wine_server_obj_handle( handle ); | |
| req->op = PULSE_EVENT; | |
| ret = wine_server_call( req ); | |
| } | |
| SERVER_END_REQ; | |
| return ret; | |
| } | |
| /****************************************************************************** | |
| * NtQueryEvent (NTDLL.@) | |
| */ | |
| NTSTATUS WINAPI NtQueryEvent ( | |
| IN HANDLE EventHandle, | |
| IN EVENT_INFORMATION_CLASS EventInformationClass, | |
| OUT PVOID EventInformation, | |
| IN ULONG EventInformationLength, | |
| OUT PULONG ReturnLength) | |
| { | |
| FIXME("(%p)\n", EventHandle); | |
| return STATUS_NOT_IMPLEMENTED; | |
| } | |
| /* | |
| * 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; | |
| DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; | |
| struct security_descriptor *sd = NULL; | |
| struct object_attributes objattr; | |
| if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; | |
| objattr.rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); | |
| objattr.sd_len = 0; | |
| objattr.name_len = len; | |
| if (attr) | |
| { | |
| status = NTDLL_create_struct_sd( attr->SecurityDescriptor, &sd, &objattr.sd_len ); | |
| if (status != STATUS_SUCCESS) return status; | |
| } | |
| SERVER_START_REQ( create_mutex ) | |
| { | |
| req->access = access; | |
| req->attributes = (attr) ? attr->Attributes : 0; | |
| req->owned = InitialOwner; | |
| wine_server_add_data( req, &objattr, sizeof(objattr) ); | |
| if (objattr.sd_len) wine_server_add_data( req, sd, objattr.sd_len ); | |
| if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); | |
| status = wine_server_call( req ); | |
| *MutantHandle = wine_server_ptr_handle( reply->handle ); | |
| } | |
| SERVER_END_REQ; | |
| NTDLL_free_struct_sd( sd ); | |
| return status; | |
| } | |
| /************************************************************************** | |
| * NtOpenMutant [NTDLL.@] | |
| * ZwOpenMutant [NTDLL.@] | |
| */ | |
| NTSTATUS WINAPI NtOpenMutant(OUT HANDLE* MutantHandle, | |
| IN ACCESS_MASK access, | |
| IN const OBJECT_ATTRIBUTES* attr ) | |
| { | |
| NTSTATUS status; | |
| DWORD len = attr && attr->ObjectName ? attr->ObjectName->Length : 0; | |
| if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; | |
| SERVER_START_REQ( open_mutex ) | |
| { | |
| req->access = access; | |
| req->attributes = (attr) ? attr->Attributes : 0; | |
| req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); | |
| if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); | |
| status = wine_server_call( req ); | |
| *MutantHandle = 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 = reply->prev_count; | |
| } | |
| SERVER_END_REQ; | |
| return status; | |
| } | |
| /****************************************************************** | |
| * NtQueryMutant [NTDLL.@] | |
| * ZwQueryMutant [NTDLL.@] | |
| */ | |
| NTSTATUS WINAPI NtQueryMutant(IN HANDLE handle, | |
| IN MUTANT_INFORMATION_CLASS MutantInformationClass, | |
| OUT PVOID MutantInformation, | |
| IN ULONG MutantInformationLength, | |
| OUT PULONG ResultLength OPTIONAL ) | |
| { | |
| FIXME("(%p %u %p %u %p): stub!\n", | |
| handle, MutantInformationClass, MutantInformation, MutantInformationLength, ResultLength); | |
| return STATUS_NOT_IMPLEMENTED; | |
| } | |
| /* | |
| * Jobs | |
| */ | |
| /****************************************************************************** | |
| * NtCreateJobObject [NTDLL.@] | |
| * ZwCreateJobObject [NTDLL.@] | |
| */ | |
| NTSTATUS WINAPI NtCreateJobObject( PHANDLE handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) | |
| { | |
| FIXME( "stub: %p %x %s\n", handle, access, attr ? debugstr_us(attr->ObjectName) : "" ); | |
| *handle = (HANDLE)0xdead; | |
| return STATUS_SUCCESS; | |
| } | |
| /****************************************************************************** | |
| * NtOpenJobObject [NTDLL.@] | |
| * ZwOpenJobObject [NTDLL.@] | |
| */ | |
| NTSTATUS WINAPI NtOpenJobObject( PHANDLE handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) | |
| { | |
| FIXME( "stub: %p %x %s\n", handle, access, attr ? debugstr_us(attr->ObjectName) : "" ); | |
| return STATUS_NOT_IMPLEMENTED; | |
| } | |
| /****************************************************************************** | |
| * NtTerminateJobObject [NTDLL.@] | |
| * ZwTerminateJobObject [NTDLL.@] | |
| */ | |
| NTSTATUS WINAPI NtTerminateJobObject( HANDLE handle, NTSTATUS status ) | |
| { | |
| FIXME( "stub: %p %x\n", handle, status ); | |
| return STATUS_SUCCESS; | |
| } | |
| /****************************************************************************** | |
| * 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 ); | |
| return STATUS_NOT_IMPLEMENTED; | |
| } | |
| /****************************************************************************** | |
| * NtSetInformationJobObject [NTDLL.@] | |
| * ZwSetInformationJobObject [NTDLL.@] | |
| */ | |
| NTSTATUS WINAPI NtSetInformationJobObject( HANDLE handle, JOBOBJECTINFOCLASS class, PVOID info, ULONG len ) | |
| { | |
| FIXME( "stub: %p %u %p %u\n", handle, class, info, len ); | |
| return STATUS_SUCCESS; | |
| } | |
| /****************************************************************************** | |
| * NtIsProcessInJob [NTDLL.@] | |
| * ZwIsProcessInJob [NTDLL.@] | |
| */ | |
| NTSTATUS WINAPI NtIsProcessInJob( HANDLE process, HANDLE job ) | |
| { | |
| FIXME( "stub: %p %p\n", process, job ); | |
| return STATUS_PROCESS_NOT_IN_JOB; | |
| } | |
| /****************************************************************************** | |
| * NtAssignProcessToJobObject [NTDLL.@] | |
| * ZwAssignProcessToJobObject [NTDLL.@] | |
| */ | |
| NTSTATUS WINAPI NtAssignProcessToJobObject( HANDLE job, HANDLE process ) | |
| { | |
| FIXME( "stub: %p %p\n", job, process ); | |
| return STATUS_SUCCESS; | |
| } | |
| /* | |
| * 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) | |
| { | |
| DWORD len = (attr && attr->ObjectName) ? attr->ObjectName->Length : 0; | |
| NTSTATUS status; | |
| if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; | |
| if (timer_type != NotificationTimer && timer_type != SynchronizationTimer) | |
| return STATUS_INVALID_PARAMETER; | |
| SERVER_START_REQ( create_timer ) | |
| { | |
| req->access = access; | |
| req->attributes = (attr) ? attr->Attributes : 0; | |
| req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); | |
| req->manual = (timer_type == NotificationTimer) ? TRUE : FALSE; | |
| if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); | |
| status = wine_server_call( req ); | |
| *handle = wine_server_ptr_handle( reply->handle ); | |
| } | |
| SERVER_END_REQ; | |
| return status; | |
| } | |
| /************************************************************************** | |
| * NtOpenTimer [NTDLL.@] | |
| * ZwOpenTimer [NTDLL.@] | |
| */ | |
| NTSTATUS WINAPI NtOpenTimer(OUT PHANDLE handle, | |
| IN ACCESS_MASK access, | |
| IN const OBJECT_ATTRIBUTES* attr ) | |
| { | |
| DWORD len = (attr && attr->ObjectName) ? attr->ObjectName->Length : 0; | |
| NTSTATUS status; | |
| if (len >= MAX_PATH * sizeof(WCHAR)) return STATUS_NAME_TOO_LONG; | |
| SERVER_START_REQ( open_timer ) | |
| { | |
| req->access = access; | |
| req->attributes = (attr) ? attr->Attributes : 0; | |
| req->rootdir = wine_server_obj_handle( attr ? attr->RootDirectory : 0 ); | |
| if (len) wine_server_add_data( req, attr->ObjectName->Buffer, len ); | |
| 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) stub\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_reply | |
| * | |
| * Wait for a reply on the waiting pipe of the current thread. | |
| */ | |
| static int wait_reply( void *cookie ) | |
| { | |
| int signaled; | |
| struct wake_up_reply reply; | |
| for (;;) | |
| { | |
| int ret; | |
| ret = read( ntdll_get_thread_data()->wait_fd[0], &reply, sizeof(reply) ); | |
| if (ret == sizeof(reply)) | |
| { | |
| if (!reply.cookie) break; /* thread got killed */ | |
| if (wine_server_get_ptr(reply.cookie) == cookie) return reply.signaled; | |
| /* we stole another reply, wait for the real one */ | |
| signaled = wait_reply( cookie ); | |
| /* and now put the wrong one back in the pipe */ | |
| for (;;) | |
| { | |
| ret = write( ntdll_get_thread_data()->wait_fd[1], &reply, sizeof(reply) ); | |
| if (ret == sizeof(reply)) break; | |
| if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret ); | |
| if (errno == EINTR) continue; | |
| server_protocol_perror("wakeup write"); | |
| } | |
| return signaled; | |
| } | |
| if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret ); | |
| if (errno == EINTR) continue; | |
| server_protocol_perror("wakeup read"); | |
| } | |
| /* the server closed the connection; time to die... */ | |
| abort_thread(0); | |
| } | |
| /*********************************************************************** | |
| * invoke_apc | |
| * | |
| * Invoke a single APC. Return TRUE if a user APC has been run. | |
| */ | |
| static BOOL invoke_apc( const apc_call_t *call, apc_result_t *result ) | |
| { | |
| BOOL user_apc = FALSE; | |
| SIZE_T size; | |
| void *addr; | |
| memset( result, 0, sizeof(*result) ); | |
| switch (call->type) | |
| { | |
| case APC_NONE: | |
| break; | |
| case APC_USER: | |
| { | |
| void (WINAPI *func)(ULONG_PTR,ULONG_PTR,ULONG_PTR) = wine_server_get_ptr( call->user.func ); | |
| func( call->user.args[0], call->user.args[1], call->user.args[2] ); | |
| user_apc = TRUE; | |
| break; | |
| } | |
| case APC_TIMER: | |
| { | |
| void (WINAPI *func)(void*, unsigned int, unsigned int) = wine_server_get_ptr( call->timer.func ); | |
| func( wine_server_get_ptr( call->timer.arg ), | |
| (DWORD)call->timer.time, (DWORD)(call->timer.time >> 32) ); | |
| user_apc = TRUE; | |
| break; | |
| } | |
| case APC_ASYNC_IO: | |
| { | |
| void *apc = NULL; | |
| IO_STATUS_BLOCK *iosb = wine_server_get_ptr( call->async_io.sb ); | |
| NTSTATUS (*func)(void *, IO_STATUS_BLOCK *, NTSTATUS, void **) = wine_server_get_ptr( call->async_io.func ); | |
| result->type = call->type; | |
| result->async_io.status = func( wine_server_get_ptr( call->async_io.user ), | |
| iosb, call->async_io.status, &apc ); | |
| if (result->async_io.status != STATUS_PENDING) | |
| { | |
| result->async_io.total = iosb->Information; | |
| result->async_io.apc = wine_server_client_ptr( apc ); | |
| } | |
| break; | |
| } | |
| case APC_VIRTUAL_ALLOC: | |
| result->type = call->type; | |
| addr = wine_server_get_ptr( call->virtual_alloc.addr ); | |
| size = call->virtual_alloc.size; | |
| if ((ULONG_PTR)addr == call->virtual_alloc.addr && size == call->virtual_alloc.size) | |
| { | |
| result->virtual_alloc.status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr, | |
| call->virtual_alloc.zero_bits, &size, | |
| call->virtual_alloc.op_type, | |
| call->virtual_alloc.prot ); | |
| result->virtual_alloc.addr = wine_server_client_ptr( addr ); | |
| result->virtual_alloc.size = size; | |
| } | |
| else result->virtual_alloc.status = STATUS_WORKING_SET_LIMIT_RANGE; | |
| break; | |
| case APC_VIRTUAL_FREE: | |
| result->type = call->type; | |
| addr = wine_server_get_ptr( call->virtual_free.addr ); | |
| size = call->virtual_free.size; | |
| if ((ULONG_PTR)addr == call->virtual_free.addr && size == call->virtual_free.size) | |
| { | |
| result->virtual_free.status = NtFreeVirtualMemory( NtCurrentProcess(), &addr, &size, | |
| call->virtual_free.op_type ); | |
| result->virtual_free.addr = wine_server_client_ptr( addr ); | |
| result->virtual_free.size = size; | |
| } | |
| else result->virtual_free.status = STATUS_INVALID_PARAMETER; | |
| break; | |
| case APC_VIRTUAL_QUERY: | |
| { | |
| MEMORY_BASIC_INFORMATION info; | |
| result->type = call->type; | |
| addr = wine_server_get_ptr( call->virtual_query.addr ); | |
| if ((ULONG_PTR)addr == call->virtual_query.addr) | |
| result->virtual_query.status = NtQueryVirtualMemory( NtCurrentProcess(), | |
| addr, MemoryBasicInformation, &info, | |
| sizeof(info), NULL ); | |
| else | |
| result->virtual_query.status = STATUS_WORKING_SET_LIMIT_RANGE; | |
| if (result->virtual_query.status == STATUS_SUCCESS) | |
| { | |
| result->virtual_query.base = wine_server_client_ptr( info.BaseAddress ); | |
| result->virtual_query.alloc_base = wine_server_client_ptr( info.AllocationBase ); | |
| result->virtual_query.size = info.RegionSize; | |
| result->virtual_query.prot = info.Protect; | |
| result->virtual_query.alloc_prot = info.AllocationProtect; | |
| result->virtual_query.state = info.State >> 12; | |
| result->virtual_query.alloc_type = info.Type >> 16; | |
| } | |
| break; | |
| } | |
| case APC_VIRTUAL_PROTECT: | |
| result->type = call->type; | |
| addr = wine_server_get_ptr( call->virtual_protect.addr ); | |
| size = call->virtual_protect.size; | |
| if ((ULONG_PTR)addr == call->virtual_protect.addr && size == call->virtual_protect.size) | |
| { | |
| result->virtual_protect.status = NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, | |
| call->virtual_protect.prot, | |
| &result->virtual_protect.prot ); | |
| result->virtual_protect.addr = wine_server_client_ptr( addr ); | |
| result->virtual_protect.size = size; | |
| } | |
| else result->virtual_protect.status = STATUS_INVALID_PARAMETER; | |
| break; | |
| case APC_VIRTUAL_FLUSH: | |
| result->type = call->type; | |
| addr = wine_server_get_ptr( call->virtual_flush.addr ); | |
| size = call->virtual_flush.size; | |
| if ((ULONG_PTR)addr == call->virtual_flush.addr && size == call->virtual_flush.size) | |
| { | |
| result->virtual_flush.status = NtFlushVirtualMemory( NtCurrentProcess(), | |
| (const void **)&addr, &size, 0 ); | |
| result->virtual_flush.addr = wine_server_client_ptr( addr ); | |
| result->virtual_flush.size = size; | |
| } | |
| else result->virtual_flush.status = STATUS_INVALID_PARAMETER; | |
| break; | |
| case APC_VIRTUAL_LOCK: | |
| result->type = call->type; | |
| addr = wine_server_get_ptr( call->virtual_lock.addr ); | |
| size = call->virtual_lock.size; | |
| if ((ULONG_PTR)addr == call->virtual_lock.addr && size == call->virtual_lock.size) | |
| { | |
| result->virtual_lock.status = NtLockVirtualMemory( NtCurrentProcess(), &addr, &size, 0 ); | |
| result->virtual_lock.addr = wine_server_client_ptr( addr ); | |
| result->virtual_lock.size = size; | |
| } | |
| else result->virtual_lock.status = STATUS_INVALID_PARAMETER; | |
| break; | |
| case APC_VIRTUAL_UNLOCK: | |
| result->type = call->type; | |
| addr = wine_server_get_ptr( call->virtual_unlock.addr ); | |
| size = call->virtual_unlock.size; | |
| if ((ULONG_PTR)addr == call->virtual_unlock.addr && size == call->virtual_unlock.size) | |
| { | |
| result->virtual_unlock.status = NtUnlockVirtualMemory( NtCurrentProcess(), &addr, &size, 0 ); | |
| result->virtual_unlock.addr = wine_server_client_ptr( addr ); | |
| result->virtual_unlock.size = size; | |
| } | |
| else result->virtual_unlock.status = STATUS_INVALID_PARAMETER; | |
| break; | |
| case APC_MAP_VIEW: | |
| result->type = call->type; | |
| addr = wine_server_get_ptr( call->map_view.addr ); | |
| size = call->map_view.size; | |
| if ((ULONG_PTR)addr == call->map_view.addr && size == call->map_view.size) | |
| { | |
| LARGE_INTEGER offset; | |
| offset.QuadPart = call->map_view.offset; | |
| result->map_view.status = NtMapViewOfSection( wine_server_ptr_handle(call->map_view.handle), | |
| NtCurrentProcess(), &addr, | |
| call->map_view.zero_bits, 0, | |
| &offset, &size, ViewShare, | |
| call->map_view.alloc_type, call->map_view.prot ); | |
| result->map_view.addr = wine_server_client_ptr( addr ); | |
| result->map_view.size = size; | |
| } | |
| else result->map_view.status = STATUS_INVALID_PARAMETER; | |
| NtClose( wine_server_ptr_handle(call->map_view.handle) ); | |
| break; | |
| case APC_UNMAP_VIEW: | |
| result->type = call->type; | |
| addr = wine_server_get_ptr( call->unmap_view.addr ); | |
| if ((ULONG_PTR)addr == call->unmap_view.addr) | |
| result->unmap_view.status = NtUnmapViewOfSection( NtCurrentProcess(), addr ); | |
| else | |
| result->unmap_view.status = STATUS_INVALID_PARAMETER; | |
| break; | |
| case APC_CREATE_THREAD: | |
| { | |
| CLIENT_ID id; | |
| HANDLE handle; | |
| SIZE_T reserve = call->create_thread.reserve; | |
| SIZE_T commit = call->create_thread.commit; | |
| void *func = wine_server_get_ptr( call->create_thread.func ); | |
| void *arg = wine_server_get_ptr( call->create_thread.arg ); | |
| result->type = call->type; | |
| if (reserve == call->create_thread.reserve && commit == call->create_thread.commit && | |
| (ULONG_PTR)func == call->create_thread.func && (ULONG_PTR)arg == call->create_thread.arg) | |
| { | |
| result->create_thread.status = RtlCreateUserThread( NtCurrentProcess(), NULL, | |
| call->create_thread.suspend, NULL, | |
| reserve, commit, func, arg, &handle, &id ); | |
| result->create_thread.handle = wine_server_obj_handle( handle ); | |
| result->create_thread.tid = HandleToULong(id.UniqueThread); | |
| } | |
| else result->create_thread.status = STATUS_INVALID_PARAMETER; | |
| break; | |
| } | |
| default: | |
| server_protocol_error( "get_apc_request: bad type %d\n", call->type ); | |
| break; | |
| } | |
| return user_apc; | |
| } | |
| /*********************************************************************** | |
| * NTDLL_queue_process_apc | |
| */ | |
| NTSTATUS NTDLL_queue_process_apc( HANDLE process, const apc_call_t *call, apc_result_t *result ) | |
| { | |
| for (;;) | |
| { | |
| NTSTATUS ret; | |
| HANDLE handle = 0; | |
| BOOL self = FALSE; | |
| SERVER_START_REQ( queue_apc ) | |
| { | |
| req->handle = wine_server_obj_handle( process ); | |
| req->call = *call; | |
| if (!(ret = wine_server_call( req ))) | |
| { | |
| handle = wine_server_ptr_handle( reply->handle ); | |
| self = reply->self; | |
| } | |
| } | |
| SERVER_END_REQ; | |
| if (ret != STATUS_SUCCESS) return ret; | |
| if (self) | |
| { | |
| invoke_apc( call, result ); | |
| } | |
| else | |
| { | |
| NtWaitForSingleObject( handle, FALSE, NULL ); | |
| SERVER_START_REQ( get_apc_result ) | |
| { | |
| req->handle = wine_server_obj_handle( handle ); | |
| if (!(ret = wine_server_call( req ))) *result = reply->result; | |
| } | |
| SERVER_END_REQ; | |
| if (!ret && result->type == APC_NONE) continue; /* APC didn't run, try again */ | |
| if (ret) NtClose( handle ); | |
| } | |
| return ret; | |
| } | |
| } | |
| /*********************************************************************** | |
| * NTDLL_wait_for_multiple_objects | |
| * | |
| * Implementation of NtWaitForMultipleObjects | |
| */ | |
| NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UINT flags, | |
| const LARGE_INTEGER *timeout, HANDLE signal_object ) | |
| { | |
| NTSTATUS ret; | |
| int i, cookie; | |
| BOOL user_apc = FALSE; | |
| obj_handle_t obj_handles[MAXIMUM_WAIT_OBJECTS]; | |
| obj_handle_t apc_handle = 0; | |
| apc_call_t call; | |
| apc_result_t result; | |
| timeout_t abs_timeout = timeout ? timeout->QuadPart : TIMEOUT_INFINITE; | |
| memset( &result, 0, sizeof(result) ); | |
| for (i = 0; i < count; i++) obj_handles[i] = wine_server_obj_handle( handles[i] ); | |
| for (;;) | |
| { | |
| SERVER_START_REQ( select ) | |
| { | |
| req->flags = flags; | |
| req->cookie = wine_server_client_ptr( &cookie ); | |
| req->signal = wine_server_obj_handle( signal_object ); | |
| req->prev_apc = apc_handle; | |
| req->timeout = abs_timeout; | |
| wine_server_add_data( req, &result, sizeof(result) ); | |
| wine_server_add_data( req, obj_handles, count * sizeof(*obj_handles) ); | |
| ret = wine_server_call( req ); | |
| abs_timeout = reply->timeout; | |
| apc_handle = reply->apc_handle; | |
| call = reply->call; | |
| } | |
| SERVER_END_REQ; | |
| if (ret == STATUS_PENDING) ret = wait_reply( &cookie ); | |
| if (ret != STATUS_USER_APC) break; | |
| if (invoke_apc( &call, &result )) | |
| { | |
| /* if we ran a user apc we have to check once more if an object got signaled, | |
| * but we don't want to wait */ | |
| abs_timeout = 0; | |
| user_apc = TRUE; | |
| } | |
| signal_object = 0; /* don't signal it multiple times */ | |
| } | |
| if (ret == STATUS_TIMEOUT && user_apc) ret = STATUS_USER_APC; | |
| /* A test on Windows 2000 shows that Windows always yields during | |
| a wait, but a wait that is hit by an event gets a priority | |
| boost as well. This seems to model that behavior the closest. */ | |
| if (ret == STATUS_TIMEOUT) NtYieldExecution(); | |
| return ret; | |
| } | |
| /* wait operations */ | |
| /****************************************************************** | |
| * NtWaitForMultipleObjects (NTDLL.@) | |
| */ | |
| NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, | |
| BOOLEAN wait_all, BOOLEAN alertable, | |
| const LARGE_INTEGER *timeout ) | |
| { | |
| UINT flags = SELECT_INTERRUPTIBLE; | |
| if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; | |
| if (wait_all) flags |= SELECT_ALL; | |
| if (alertable) flags |= SELECT_ALERTABLE; | |
| return NTDLL_wait_for_multiple_objects( count, handles, flags, timeout, 0 ); | |
| } | |
| /****************************************************************** | |
| * NtWaitForSingleObject (NTDLL.@) | |
| */ | |
| NTSTATUS WINAPI NtWaitForSingleObject(HANDLE handle, BOOLEAN alertable, const LARGE_INTEGER *timeout ) | |
| { | |
| return NtWaitForMultipleObjects( 1, &handle, FALSE, alertable, timeout ); | |
| } | |
| /****************************************************************** | |
| * NtSignalAndWaitForSingleObject (NTDLL.@) | |
| */ | |
| NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE hSignalObject, HANDLE hWaitObject, | |
| BOOLEAN alertable, const LARGE_INTEGER *timeout ) | |
| { | |
| UINT flags = SELECT_INTERRUPTIBLE; | |
| if (!hSignalObject) return STATUS_INVALID_HANDLE; | |
| if (alertable) flags |= SELECT_ALERTABLE; | |
| return NTDLL_wait_for_multiple_objects( 1, &hWaitObject, flags, timeout, hSignalObject ); | |
| } | |
| /****************************************************************** | |
| * 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 NTDLL_wait_for_multiple_objects( 0, NULL, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, | |
| timeout, 0 ); | |
| 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; | |
| } | |
| /****************************************************************** | |
| * 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 ObjectAttributes, ULONG NumberOfConcurrentThreads ) | |
| { | |
| NTSTATUS status; | |
| TRACE("(%p, %x, %p, %d)\n", CompletionPort, DesiredAccess, | |
| ObjectAttributes, NumberOfConcurrentThreads); | |
| if (!CompletionPort) | |
| return STATUS_INVALID_PARAMETER; | |
| SERVER_START_REQ( create_completion ) | |
| { | |
| req->access = DesiredAccess; | |
| req->attributes = ObjectAttributes ? ObjectAttributes->Attributes : 0; | |
| req->rootdir = wine_server_obj_handle( ObjectAttributes ? ObjectAttributes->RootDirectory : 0 ); | |
| req->concurrent = NumberOfConcurrentThreads; | |
| if (ObjectAttributes && ObjectAttributes->ObjectName) | |
| wine_server_add_data( req, ObjectAttributes->ObjectName->Buffer, | |
| ObjectAttributes->ObjectName->Length ); | |
| if (!(status = wine_server_call( req ))) | |
| *CompletionPort = wine_server_ptr_handle( reply->handle ); | |
| } | |
| SERVER_END_REQ; | |
| 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, | |
| ULONG NumberOfBytesTransferred ) | |
| { | |
| NTSTATUS status; | |
| TRACE("(%p, %lx, %lx, %x, %d)\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; | |
| } | |
| /****************************************************************** | |
| * 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( PHANDLE CompletionPort, ACCESS_MASK DesiredAccess, | |
| POBJECT_ATTRIBUTES ObjectAttributes ) | |
| { | |
| NTSTATUS status; | |
| TRACE("(%p, 0x%x, %p)\n", CompletionPort, DesiredAccess, ObjectAttributes); | |
| if (!CompletionPort || !ObjectAttributes || !ObjectAttributes->ObjectName) | |
| return STATUS_INVALID_PARAMETER; | |
| SERVER_START_REQ( open_completion ) | |
| { | |
| req->access = DesiredAccess; | |
| req->rootdir = wine_server_obj_handle( ObjectAttributes->RootDirectory ); | |
| wine_server_add_data( req, ObjectAttributes->ObjectName->Buffer, | |
| ObjectAttributes->ObjectName->Length ); | |
| if (!(status = wine_server_call( req ))) | |
| *CompletionPort = 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 ) | |
| { | |
| NTSTATUS status; | |
| SERVER_START_REQ( add_fd_completion ) | |
| { | |
| req->handle = wine_server_obj_handle( hFile ); | |
| req->cvalue = CompletionValue; | |
| req->status = CompletionStatus; | |
| req->information = Information; | |
| status = wine_server_call( req ); | |
| } | |
| SERVER_END_REQ; | |
| return status; | |
| } |