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