Sweden-Number/dlls/ntoskrnl.exe/sync.c

1199 lines
36 KiB
C

/*
* Kernel synchronization
*
* Copyright (C) 2018 Zebediah Figura
*
* 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 <limits.h>
#include <stdarg.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winbase.h"
#include "winternl.h"
#include "ddk/ntddk.h"
#include "ddk/wdm.h"
#include "ddk/ntifs.h"
#include "wine/asm.h"
#include "wine/debug.h"
#include "wine/heap.h"
#include "ntoskrnl_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(ntoskrnl);
enum object_type
{
TYPE_MANUAL_EVENT = 0,
TYPE_AUTO_EVENT = 1,
TYPE_MUTEX = 2,
TYPE_SEMAPHORE = 5,
TYPE_MANUAL_TIMER = 8,
TYPE_AUTO_TIMER = 9,
};
DECLARE_CRITICAL_SECTION(sync_cs);
/***********************************************************************
* KeWaitForMultipleObjects (NTOSKRNL.EXE.@)
*/
NTSTATUS WINAPI KeWaitForMultipleObjects(ULONG count, void *pobjs[],
WAIT_TYPE wait_type, KWAIT_REASON reason, KPROCESSOR_MODE mode,
BOOLEAN alertable, LARGE_INTEGER *timeout, KWAIT_BLOCK *wait_blocks)
{
DISPATCHER_HEADER **objs = (DISPATCHER_HEADER **)pobjs;
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
NTSTATUS ret;
ULONG i;
TRACE("count %u, objs %p, wait_type %u, reason %u, mode %d, alertable %u, timeout %p, wait_blocks %p.\n",
count, objs, wait_type, reason, mode, alertable, timeout, wait_blocks);
/* We co-opt DISPATCHER_HEADER.WaitListHead:
* Blink stores a handle to the synchronization object,
* Flink stores the number of threads currently waiting on this object. */
EnterCriticalSection( &sync_cs );
for (i = 0; i < count; i++)
{
if (objs[i]->WaitListHead.Blink == INVALID_HANDLE_VALUE)
{
ObOpenObjectByPointer( objs[i], OBJ_KERNEL_HANDLE, NULL, SYNCHRONIZE, NULL, KernelMode, &handles[i] );
continue;
}
++*((ULONG_PTR *)&objs[i]->WaitListHead.Flink);
if (!objs[i]->WaitListHead.Blink)
{
switch (objs[i]->Type)
{
case TYPE_MANUAL_EVENT:
objs[i]->WaitListHead.Blink = CreateEventW( NULL, TRUE, objs[i]->SignalState, NULL );
break;
case TYPE_AUTO_EVENT:
objs[i]->WaitListHead.Blink = CreateEventW( NULL, FALSE, objs[i]->SignalState, NULL );
break;
case TYPE_MUTEX:
objs[i]->WaitListHead.Blink = CreateMutexW( NULL, FALSE, NULL );
break;
case TYPE_SEMAPHORE:
{
KSEMAPHORE *semaphore = CONTAINING_RECORD(objs[i], KSEMAPHORE, Header);
objs[i]->WaitListHead.Blink = CreateSemaphoreW( NULL,
semaphore->Header.SignalState, semaphore->Limit, NULL );
break;
}
case TYPE_MANUAL_TIMER:
case TYPE_AUTO_TIMER:
break;
}
}
handles[i] = objs[i]->WaitListHead.Blink;
}
LeaveCriticalSection( &sync_cs );
ret = NtWaitForMultipleObjects( count, handles, (wait_type == WaitAny), alertable, timeout );
EnterCriticalSection( &sync_cs );
for (i = 0; i < count; i++)
{
if (ret == i || (!ret && wait_type == WaitAll))
{
switch (objs[i]->Type)
{
case TYPE_AUTO_EVENT:
case TYPE_AUTO_TIMER:
objs[i]->SignalState = FALSE;
break;
case TYPE_MUTEX:
case TYPE_SEMAPHORE:
--objs[i]->SignalState;
break;
}
}
if (objs[i]->WaitListHead.Blink == INVALID_HANDLE_VALUE)
{
NtClose( handles[i] );
}
else if (!--*((ULONG_PTR *)&objs[i]->WaitListHead.Flink))
{
switch (objs[i]->Type)
{
case TYPE_MANUAL_EVENT:
case TYPE_AUTO_EVENT:
case TYPE_SEMAPHORE:
CloseHandle(objs[i]->WaitListHead.Blink);
objs[i]->WaitListHead.Blink = NULL;
break;
case TYPE_MUTEX:
/* Native will panic if a mutex is destroyed while held, so we
* don't have to worry about leaking the handle here. */
if (objs[i]->SignalState == 1)
{
CloseHandle(objs[i]->WaitListHead.Blink);
objs[i]->WaitListHead.Blink = NULL;
}
break;
}
}
}
LeaveCriticalSection( &sync_cs );
return ret;
}
/***********************************************************************
* KeWaitForSingleObject (NTOSKRNL.EXE.@)
*/
NTSTATUS WINAPI KeWaitForSingleObject( void *obj, KWAIT_REASON reason,
KPROCESSOR_MODE mode, BOOLEAN alertable, LARGE_INTEGER *timeout )
{
return KeWaitForMultipleObjects( 1, &obj, WaitAny, reason, mode, alertable, timeout, NULL );
}
/***********************************************************************
* KeWaitForMutexObject (NTOSKRNL.EXE.@)
*/
NTSTATUS WINAPI KeWaitForMutexObject( PRKMUTEX mutex, KWAIT_REASON reason,
KPROCESSOR_MODE mode, BOOLEAN alertable, LARGE_INTEGER *timeout)
{
return KeWaitForSingleObject( mutex, reason, mode, alertable, timeout );
}
/***********************************************************************
* KeInitializeEvent (NTOSKRNL.EXE.@)
*/
void WINAPI KeInitializeEvent( PRKEVENT event, EVENT_TYPE type, BOOLEAN state )
{
TRACE("event %p, type %u, state %u.\n", event, type, state);
event->Header.Type = type;
event->Header.SignalState = state;
event->Header.WaitListHead.Blink = NULL;
event->Header.WaitListHead.Flink = NULL;
}
static void *create_event_object( HANDLE handle )
{
EVENT_BASIC_INFORMATION info;
KEVENT *event;
if (!(event = alloc_kernel_object( ExEventObjectType, handle, sizeof(*event), 0 ))) return NULL;
if (!NtQueryEvent( handle, EventBasicInformation, &info, sizeof(info), NULL ))
KeInitializeEvent( event, info.EventType, info.EventState );
event->Header.WaitListHead.Blink = INVALID_HANDLE_VALUE; /* mark as kernel object */
return event;
}
static const WCHAR event_type_name[] = {'E','v','e','n','t',0};
static struct _OBJECT_TYPE event_type = {
event_type_name,
create_event_object
};
POBJECT_TYPE ExEventObjectType = &event_type;
/***********************************************************************
* IoCreateSynchronizationEvent (NTOSKRNL.EXE.@)
*/
PKEVENT WINAPI IoCreateSynchronizationEvent( UNICODE_STRING *name, HANDLE *ret_handle )
{
OBJECT_ATTRIBUTES attr;
HANDLE handle;
KEVENT *event;
NTSTATUS ret;
TRACE( "(%p %p)\n", name, ret_handle );
InitializeObjectAttributes( &attr, name, 0, 0, NULL );
ret = NtCreateEvent( &handle, EVENT_ALL_ACCESS, &attr, SynchronizationEvent, TRUE );
if (ret) return NULL;
if (kernel_object_from_handle( handle, ExEventObjectType, (void**)&event ))
{
NtClose( handle);
return NULL;
}
*ret_handle = handle;
return event;
}
/***********************************************************************
* KeSetEvent (NTOSKRNL.EXE.@)
*/
LONG WINAPI KeSetEvent( PRKEVENT event, KPRIORITY increment, BOOLEAN wait )
{
HANDLE handle;
LONG ret = 0;
TRACE("event %p, increment %d, wait %u.\n", event, increment, wait);
if (event->Header.WaitListHead.Blink != INVALID_HANDLE_VALUE)
{
EnterCriticalSection( &sync_cs );
ret = InterlockedExchange( &event->Header.SignalState, TRUE );
if ((handle = event->Header.WaitListHead.Blink))
SetEvent( handle );
LeaveCriticalSection( &sync_cs );
}
else
{
if (!ObOpenObjectByPointer( event, OBJ_KERNEL_HANDLE, NULL, EVENT_MODIFY_STATE, NULL, KernelMode, &handle ))
{
NtSetEvent( handle, &ret );
NtClose( handle );
}
event->Header.SignalState = TRUE;
}
return ret;
}
/***********************************************************************
* KeResetEvent (NTOSKRNL.EXE.@)
*/
LONG WINAPI KeResetEvent( PRKEVENT event )
{
HANDLE handle;
LONG ret = 0;
TRACE("event %p.\n", event);
if (event->Header.WaitListHead.Blink != INVALID_HANDLE_VALUE)
{
EnterCriticalSection( &sync_cs );
ret = InterlockedExchange( &event->Header.SignalState, FALSE );
if ((handle = event->Header.WaitListHead.Blink))
ResetEvent( handle );
LeaveCriticalSection( &sync_cs );
}
else
{
if (!ObOpenObjectByPointer( event, OBJ_KERNEL_HANDLE, NULL, EVENT_MODIFY_STATE, NULL, KernelMode, &handle ))
{
NtResetEvent( handle, &ret );
NtClose( handle );
}
event->Header.SignalState = FALSE;
}
return ret;
}
/***********************************************************************
* KeClearEvent (NTOSKRNL.EXE.@)
*/
void WINAPI KeClearEvent( PRKEVENT event )
{
KeResetEvent( event );
}
/***********************************************************************
* KeInitializeSemaphore (NTOSKRNL.EXE.@)
*/
void WINAPI KeInitializeSemaphore( PRKSEMAPHORE semaphore, LONG count, LONG limit )
{
TRACE("semaphore %p, count %d, limit %d.\n", semaphore, count, limit);
semaphore->Header.Type = TYPE_SEMAPHORE;
semaphore->Header.SignalState = count;
semaphore->Header.WaitListHead.Blink = NULL;
semaphore->Header.WaitListHead.Flink = NULL;
semaphore->Limit = limit;
}
/***********************************************************************
* KeReleaseSemaphore (NTOSKRNL.EXE.@)
*/
LONG WINAPI KeReleaseSemaphore( PRKSEMAPHORE semaphore, KPRIORITY increment,
LONG count, BOOLEAN wait )
{
HANDLE handle;
LONG ret;
TRACE("semaphore %p, increment %d, count %d, wait %u.\n",
semaphore, increment, count, wait);
EnterCriticalSection( &sync_cs );
ret = InterlockedExchangeAdd( &semaphore->Header.SignalState, count );
if ((handle = semaphore->Header.WaitListHead.Blink))
ReleaseSemaphore( handle, count, NULL );
LeaveCriticalSection( &sync_cs );
return ret;
}
static const WCHAR semaphore_type_name[] = {'S','e','m','a','p','h','o','r','e',0};
static struct _OBJECT_TYPE semaphore_type =
{
semaphore_type_name
};
POBJECT_TYPE ExSemaphoreObjectType = &semaphore_type;
/***********************************************************************
* KeInitializeMutex (NTOSKRNL.EXE.@)
*/
void WINAPI KeInitializeMutex( PRKMUTEX mutex, ULONG level )
{
TRACE("mutex %p, level %u.\n", mutex, level);
mutex->Header.Type = TYPE_MUTEX;
mutex->Header.SignalState = 1;
mutex->Header.WaitListHead.Blink = NULL;
mutex->Header.WaitListHead.Flink = NULL;
}
/***********************************************************************
* KeReleaseMutex (NTOSKRNL.EXE.@)
*/
LONG WINAPI KeReleaseMutex( PRKMUTEX mutex, BOOLEAN wait )
{
LONG ret;
TRACE("mutex %p, wait %u.\n", mutex, wait);
EnterCriticalSection( &sync_cs );
ret = mutex->Header.SignalState++;
if (!ret && !mutex->Header.WaitListHead.Flink)
{
CloseHandle( mutex->Header.WaitListHead.Blink );
mutex->Header.WaitListHead.Blink = NULL;
}
LeaveCriticalSection( &sync_cs );
return ret;
}
/***********************************************************************
* KeInitializeTimerEx (NTOSKRNL.EXE.@)
*/
void WINAPI KeInitializeTimerEx( KTIMER *timer, TIMER_TYPE type )
{
TRACE("timer %p, type %u.\n", timer, type);
RtlZeroMemory(timer, sizeof(KTIMER));
timer->Header.Type = (type == NotificationTimer) ? TYPE_MANUAL_TIMER : TYPE_AUTO_TIMER;
timer->Header.SignalState = FALSE;
timer->Header.Inserted = FALSE;
timer->Header.WaitListHead.Blink = NULL;
timer->Header.WaitListHead.Flink = NULL;
}
/***********************************************************************
* KeInitializeTimer (NTOSKRNL.EXE.@)
*/
void WINAPI KeInitializeTimer( KTIMER *timer )
{
KeInitializeTimerEx(timer, NotificationTimer);
}
/***********************************************************************
* KeSetTimerEx (NTOSKRNL.EXE.@)
*/
BOOLEAN WINAPI KeSetTimerEx( KTIMER *timer, LARGE_INTEGER duetime, LONG period, KDPC *dpc )
{
BOOL ret;
TRACE("timer %p, duetime %s, period %d, dpc %p.\n",
timer, wine_dbgstr_longlong(duetime.QuadPart), period, dpc);
if (dpc)
{
FIXME("Unhandled DPC %p.\n", dpc);
return FALSE;
}
EnterCriticalSection( &sync_cs );
ret = timer->Header.Inserted;
timer->Header.Inserted = TRUE;
timer->Header.WaitListHead.Blink = CreateWaitableTimerW( NULL, timer->Header.Type == TYPE_MANUAL_TIMER, NULL );
SetWaitableTimer( timer->Header.WaitListHead.Blink, &duetime, period, NULL, NULL, FALSE );
LeaveCriticalSection( &sync_cs );
return ret;
}
BOOLEAN WINAPI KeCancelTimer( KTIMER *timer )
{
BOOL ret;
TRACE("timer %p.\n", timer);
EnterCriticalSection( &sync_cs );
ret = timer->Header.Inserted;
timer->Header.Inserted = FALSE;
CloseHandle(timer->Header.WaitListHead.Blink);
timer->Header.WaitListHead.Blink = NULL;
LeaveCriticalSection( &sync_cs );
return ret;
}
/***********************************************************************
* KeDelayExecutionThread (NTOSKRNL.EXE.@)
*/
NTSTATUS WINAPI KeDelayExecutionThread( KPROCESSOR_MODE mode, BOOLEAN alertable, LARGE_INTEGER *timeout )
{
TRACE("mode %d, alertable %u, timeout %p.\n", mode, alertable, timeout);
return NtDelayExecution( alertable, timeout );
}
/***********************************************************************
* KeInitializeSpinLock (NTOSKRNL.EXE.@)
*/
void WINAPI KeInitializeSpinLock( KSPIN_LOCK *lock )
{
TRACE("lock %p.\n", lock);
*lock = 0;
}
static inline void small_pause(void)
{
#ifdef __x86_64__
__asm__ __volatile__( "rep;nop" : : : "memory" );
#else
__asm__ __volatile__( "" : : : "memory" );
#endif
}
/***********************************************************************
* KeAcquireSpinLockAtDpcLevel (NTOSKRNL.EXE.@)
*/
void WINAPI KeAcquireSpinLockAtDpcLevel( KSPIN_LOCK *lock )
{
TRACE("lock %p.\n", lock);
while (!InterlockedCompareExchangePointer( (void **)lock, (void *)1, (void *)0 ))
small_pause();
}
/***********************************************************************
* KeReleaseSpinLockFromDpcLevel (NTOSKRNL.EXE.@)
*/
void WINAPI KeReleaseSpinLockFromDpcLevel( KSPIN_LOCK *lock )
{
TRACE("lock %p.\n", lock);
InterlockedExchangePointer( (void **)lock, 0 );
}
#define QUEUED_SPINLOCK_OWNED 0x2
/***********************************************************************
* KeAcquireInStackQueuedSpinLockAtDpcLevel (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL_WRAPPER( KeAcquireInStackQueuedSpinLockAtDpcLevel, 8 )
void FASTCALL KeAcquireInStackQueuedSpinLockAtDpcLevel( KSPIN_LOCK *lock, KLOCK_QUEUE_HANDLE *queue )
{
KSPIN_LOCK_QUEUE *tail;
TRACE("lock %p, queue %p.\n", lock, queue);
queue->LockQueue.Next = NULL;
if (!(tail = InterlockedExchangePointer( (void **)lock, &queue->LockQueue )))
queue->LockQueue.Lock = (KSPIN_LOCK *)((ULONG_PTR)lock | QUEUED_SPINLOCK_OWNED);
else
{
queue->LockQueue.Lock = lock;
InterlockedExchangePointer( (void **)&tail->Next, &queue->LockQueue );
while (!((ULONG_PTR)InterlockedCompareExchangePointer( (void **)&queue->LockQueue.Lock, 0, 0 )
& QUEUED_SPINLOCK_OWNED))
{
small_pause();
}
}
}
/***********************************************************************
* KeReleaseInStackQueuedSpinLockFromDpcLevel (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL1_WRAPPER( KeReleaseInStackQueuedSpinLockFromDpcLevel )
void FASTCALL KeReleaseInStackQueuedSpinLockFromDpcLevel( KLOCK_QUEUE_HANDLE *queue )
{
KSPIN_LOCK *lock = (KSPIN_LOCK *)((ULONG_PTR)queue->LockQueue.Lock & ~QUEUED_SPINLOCK_OWNED);
KSPIN_LOCK_QUEUE *next;
TRACE("lock %p, queue %p.\n", lock, queue);
queue->LockQueue.Lock = NULL;
if (!(next = queue->LockQueue.Next))
{
/* If we are truly the last in the queue, the lock will point to us. */
if (InterlockedCompareExchangePointer( (void **)lock, NULL, &queue->LockQueue ) == queue)
return;
/* Otherwise, someone just queued themselves, but hasn't yet set
* themselves as successor. Spin waiting for them to do so. */
while (!(next = queue->LockQueue.Next))
small_pause();
}
InterlockedExchangePointer( (void **)&next->Lock, (KSPIN_LOCK *)((ULONG_PTR)lock | QUEUED_SPINLOCK_OWNED) );
}
#ifndef __i386__
/***********************************************************************
* KeReleaseSpinLock (NTOSKRNL.EXE.@)
*/
void WINAPI KeReleaseSpinLock( KSPIN_LOCK *lock, KIRQL irql )
{
TRACE("lock %p, irql %u.\n", lock, irql);
KeReleaseSpinLockFromDpcLevel( lock );
}
/***********************************************************************
* KeAcquireSpinLockRaiseToDpc (NTOSKRNL.EXE.@)
*/
KIRQL WINAPI KeAcquireSpinLockRaiseToDpc( KSPIN_LOCK *lock )
{
TRACE("lock %p.\n", lock);
KeAcquireSpinLockAtDpcLevel( lock );
return 0;
}
/***********************************************************************
* KeAcquireInStackQueuedSpinLock (NTOSKRNL.EXE.@)
*/
void WINAPI KeAcquireInStackQueuedSpinLock( KSPIN_LOCK *lock, KLOCK_QUEUE_HANDLE *queue )
{
TRACE("lock %p, queue %p.\n", lock, queue);
KeAcquireInStackQueuedSpinLockAtDpcLevel( lock, queue );
}
/***********************************************************************
* KeReleaseInStackQueuedSpinLock (NTOSKRNL.EXE.@)
*/
void WINAPI KeReleaseInStackQueuedSpinLock( KLOCK_QUEUE_HANDLE *queue )
{
TRACE("queue %p.\n", queue);
KeReleaseInStackQueuedSpinLockFromDpcLevel( queue );
}
#endif
static KSPIN_LOCK cancel_lock;
/***********************************************************************
* IoAcquireCancelSpinLock (NTOSKRNL.EXE.@)
*/
void WINAPI IoAcquireCancelSpinLock( KIRQL *irql )
{
TRACE("irql %p.\n", irql);
KeAcquireSpinLock( &cancel_lock, irql );
}
/***********************************************************************
* IoReleaseCancelSpinLock (NTOSKRNL.EXE.@)
*/
void WINAPI IoReleaseCancelSpinLock( KIRQL irql )
{
TRACE("irql %u.\n", irql);
KeReleaseSpinLock( &cancel_lock, irql );
}
/***********************************************************************
* ExfInterlockedRemoveHeadList (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL_WRAPPER( ExfInterlockedRemoveHeadList, 8 )
PLIST_ENTRY FASTCALL ExfInterlockedRemoveHeadList( LIST_ENTRY *list, KSPIN_LOCK *lock )
{
return ExInterlockedRemoveHeadList( list, lock );
}
/***********************************************************************
* ExInterlockedRemoveHeadList (NTOSKRNL.EXE.@)
*/
LIST_ENTRY * WINAPI ExInterlockedRemoveHeadList( LIST_ENTRY *list, KSPIN_LOCK *lock )
{
LIST_ENTRY *ret;
KIRQL irql;
TRACE("list %p, lock %p.\n", list, lock);
KeAcquireSpinLock( lock, &irql );
ret = RemoveHeadList( list );
KeReleaseSpinLock( lock, irql );
return ret;
}
/***********************************************************************
* InterlockedPopEntrySList (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL1_WRAPPER( NTOSKRNL_InterlockedPopEntrySList )
PSLIST_ENTRY FASTCALL NTOSKRNL_InterlockedPopEntrySList( PSLIST_HEADER list )
{
return RtlInterlockedPopEntrySList( list );
}
/***********************************************************************
* InterlockedPushEntrySList (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL_WRAPPER( NTOSKRNL_InterlockedPushEntrySList, 8 )
PSLIST_ENTRY FASTCALL NTOSKRNL_InterlockedPushEntrySList( PSLIST_HEADER list, PSLIST_ENTRY entry )
{
return RtlInterlockedPushEntrySList( list, entry );
}
/***********************************************************************
* ExInterlockedPopEntrySList (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL_WRAPPER( NTOSKRNL_ExInterlockedPopEntrySList, 8 )
PSLIST_ENTRY FASTCALL NTOSKRNL_ExInterlockedPopEntrySList( PSLIST_HEADER list, PKSPIN_LOCK lock )
{
return RtlInterlockedPopEntrySList( list );
}
/***********************************************************************
* ExInterlockedPushEntrySList (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL_WRAPPER( NTOSKRNL_ExInterlockedPushEntrySList, 12 )
PSLIST_ENTRY FASTCALL NTOSKRNL_ExInterlockedPushEntrySList( PSLIST_HEADER list, PSLIST_ENTRY entry, PKSPIN_LOCK lock )
{
return RtlInterlockedPushEntrySList( list, entry );
}
/***********************************************************************
* ExInterlockedFlushSList (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL1_WRAPPER( NTOSKRNL_ExInterlockedFlushSList )
PSLIST_ENTRY FASTCALL NTOSKRNL_ExInterlockedFlushSList( PSLIST_HEADER list )
{
return RtlInterlockedFlushSList( list );
}
/***********************************************************************
* ExAcquireFastMutexUnsafe (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL1_WRAPPER(ExAcquireFastMutexUnsafe)
void FASTCALL ExAcquireFastMutexUnsafe( FAST_MUTEX *mutex )
{
LONG count;
TRACE("mutex %p.\n", mutex);
count = InterlockedDecrement( &mutex->Count );
if (count < 0)
KeWaitForSingleObject( &mutex->Event, Executive, KernelMode, FALSE, NULL );
}
/***********************************************************************
* ExReleaseFastMutexUnsafe (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL1_WRAPPER(ExReleaseFastMutexUnsafe)
void FASTCALL ExReleaseFastMutexUnsafe( FAST_MUTEX *mutex )
{
LONG count;
TRACE("mutex %p.\n", mutex);
count = InterlockedIncrement( &mutex->Count );
if (count < 1)
KeSetEvent( &mutex->Event, IO_NO_INCREMENT, FALSE );
}
#ifndef __i386__
/***********************************************************************
* ExAcquireFastMutex (NTOSKRNL.@)
*/
void WINAPI ExAcquireFastMutex( FAST_MUTEX *mutex )
{
/* FIXME: lower IRQL */
ExAcquireFastMutexUnsafe( mutex );
}
/***********************************************************************
* ExReleaseFastMutex (NTOSKRNL.@)
*/
void WINAPI ExReleaseFastMutex( FAST_MUTEX *mutex )
{
ExReleaseFastMutexUnsafe( mutex );
/* FIXME: restore IRQL */
}
#endif /* __i386__ */
/* Use of the fields of an ERESOURCE structure seems to vary wildly between
* Windows versions. The below implementation uses them as follows:
*
* OwnerTable - contains a list of shared owners, including threads which do
* not currently own the resource
* OwnerTable[i].OwnerThread - shared owner TID
* OwnerTable[i].OwnerCount - recursion count of this shared owner (may be 0)
* OwnerEntry.OwnerThread - the owner TID if exclusively owned
* OwnerEntry.TableSize - the number of entries in OwnerTable, including threads
* which do not currently own the resource
* ActiveEntries - total number of acquisitions (incl. recursive ones)
*/
/***********************************************************************
* ExInitializeResourceLite (NTOSKRNL.EXE.@)
*/
NTSTATUS WINAPI ExInitializeResourceLite( ERESOURCE *resource )
{
TRACE("resource %p.\n", resource);
memset(resource, 0, sizeof(*resource));
return STATUS_SUCCESS;
}
/***********************************************************************
* ExDeleteResourceLite (NTOSKRNL.EXE.@)
*/
NTSTATUS WINAPI ExDeleteResourceLite( ERESOURCE *resource )
{
TRACE("resource %p.\n", resource);
heap_free(resource->OwnerTable);
heap_free(resource->ExclusiveWaiters);
heap_free(resource->SharedWaiters);
return STATUS_SUCCESS;
}
/* Find an existing entry in the shared owner list, or create a new one. */
static OWNER_ENTRY *resource_get_shared_entry( ERESOURCE *resource, ERESOURCE_THREAD thread )
{
ULONG i, count;
for (i = 0; i < resource->OwnerEntry.TableSize; ++i)
{
if (resource->OwnerTable[i].OwnerThread == thread)
return &resource->OwnerTable[i];
}
count = ++resource->OwnerEntry.TableSize;
resource->OwnerTable = heap_realloc(resource->OwnerTable, count * sizeof(*resource->OwnerTable));
resource->OwnerTable[count - 1].OwnerThread = thread;
resource->OwnerTable[count - 1].OwnerCount = 0;
return &resource->OwnerTable[count - 1];
}
/***********************************************************************
* ExAcquireResourceExclusiveLite (NTOSKRNL.EXE.@)
*/
BOOLEAN WINAPI ExAcquireResourceExclusiveLite( ERESOURCE *resource, BOOLEAN wait )
{
KIRQL irql;
TRACE("resource %p, wait %u.\n", resource, wait);
KeAcquireSpinLock( &resource->SpinLock, &irql );
if (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread())
{
resource->ActiveEntries++;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
/* In order to avoid a race between waiting for the ExclusiveWaiters event
* and grabbing the lock, do not grab the resource if it is unclaimed but
* has waiters; instead queue ourselves. */
else if (!resource->ActiveEntries && !resource->NumberOfExclusiveWaiters && !resource->NumberOfSharedWaiters)
{
resource->Flag |= ResourceOwnedExclusive;
resource->OwnerEntry.OwnerThread = (ERESOURCE_THREAD)KeGetCurrentThread();
resource->ActiveEntries++;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
else if (!wait)
{
KeReleaseSpinLock( &resource->SpinLock, irql );
return FALSE;
}
if (!resource->ExclusiveWaiters)
{
resource->ExclusiveWaiters = heap_alloc( sizeof(*resource->ExclusiveWaiters) );
KeInitializeEvent( resource->ExclusiveWaiters, SynchronizationEvent, FALSE );
}
resource->NumberOfExclusiveWaiters++;
KeReleaseSpinLock( &resource->SpinLock, irql );
KeWaitForSingleObject( resource->ExclusiveWaiters, Executive, KernelMode, FALSE, NULL );
KeAcquireSpinLock( &resource->SpinLock, &irql );
resource->Flag |= ResourceOwnedExclusive;
resource->OwnerEntry.OwnerThread = (ERESOURCE_THREAD)KeGetCurrentThread();
resource->ActiveEntries++;
resource->NumberOfExclusiveWaiters--;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
/***********************************************************************
* ExAcquireResourceSharedLite (NTOSKRNL.EXE.@)
*/
BOOLEAN WINAPI ExAcquireResourceSharedLite( ERESOURCE *resource, BOOLEAN wait )
{
OWNER_ENTRY *entry;
KIRQL irql;
TRACE("resource %p, wait %u.\n", resource, wait);
KeAcquireSpinLock( &resource->SpinLock, &irql );
entry = resource_get_shared_entry( resource, (ERESOURCE_THREAD)KeGetCurrentThread() );
if (resource->Flag & ResourceOwnedExclusive)
{
if (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread())
{
/* We own the resource exclusively, so increase recursion. */
resource->ActiveEntries++;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
}
else if (entry->OwnerCount || !resource->NumberOfExclusiveWaiters)
{
/* Either we already own the resource shared, or there are no exclusive
* owners or waiters, so we can grab it shared. */
entry->OwnerCount++;
resource->ActiveEntries++;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
if (!wait)
{
KeReleaseSpinLock( &resource->SpinLock, irql );
return FALSE;
}
if (!resource->SharedWaiters)
{
resource->SharedWaiters = heap_alloc( sizeof(*resource->SharedWaiters) );
KeInitializeSemaphore( resource->SharedWaiters, 0, INT_MAX );
}
resource->NumberOfSharedWaiters++;
KeReleaseSpinLock( &resource->SpinLock, irql );
KeWaitForSingleObject( resource->SharedWaiters, Executive, KernelMode, FALSE, NULL );
KeAcquireSpinLock( &resource->SpinLock, &irql );
entry->OwnerCount++;
resource->ActiveEntries++;
resource->NumberOfSharedWaiters--;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
/***********************************************************************
* ExAcquireSharedStarveExclusive (NTOSKRNL.EXE.@)
*/
BOOLEAN WINAPI ExAcquireSharedStarveExclusive( ERESOURCE *resource, BOOLEAN wait )
{
OWNER_ENTRY *entry;
KIRQL irql;
TRACE("resource %p, wait %u.\n", resource, wait);
KeAcquireSpinLock( &resource->SpinLock, &irql );
entry = resource_get_shared_entry( resource, (ERESOURCE_THREAD)KeGetCurrentThread() );
if (resource->Flag & ResourceOwnedExclusive)
{
if (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread())
{
resource->ActiveEntries++;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
}
/* We are starving exclusive waiters, but we cannot steal the resource out
* from under an exclusive waiter who is about to acquire it. (Because of
* locking, and because exclusive waiters are always waked first, this is
* guaranteed to be the case if the resource is unowned and there are
* exclusive waiters.) */
else if (!(!resource->ActiveEntries && resource->NumberOfExclusiveWaiters))
{
entry->OwnerCount++;
resource->ActiveEntries++;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
if (!wait)
{
KeReleaseSpinLock( &resource->SpinLock, irql );
return FALSE;
}
if (!resource->SharedWaiters)
{
resource->SharedWaiters = heap_alloc( sizeof(*resource->SharedWaiters) );
KeInitializeSemaphore( resource->SharedWaiters, 0, INT_MAX );
}
resource->NumberOfSharedWaiters++;
KeReleaseSpinLock( &resource->SpinLock, irql );
KeWaitForSingleObject( resource->SharedWaiters, Executive, KernelMode, FALSE, NULL );
KeAcquireSpinLock( &resource->SpinLock, &irql );
entry->OwnerCount++;
resource->ActiveEntries++;
resource->NumberOfSharedWaiters--;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
/***********************************************************************
* ExAcquireSharedWaitForExclusive (NTOSKRNL.EXE.@)
*/
BOOLEAN WINAPI ExAcquireSharedWaitForExclusive( ERESOURCE *resource, BOOLEAN wait )
{
OWNER_ENTRY *entry;
KIRQL irql;
TRACE("resource %p, wait %u.\n", resource, wait);
KeAcquireSpinLock( &resource->SpinLock, &irql );
entry = resource_get_shared_entry( resource, (ERESOURCE_THREAD)KeGetCurrentThread() );
if (resource->Flag & ResourceOwnedExclusive)
{
if (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread())
{
/* We own the resource exclusively, so increase recursion. */
resource->ActiveEntries++;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
}
/* We may only grab the resource if there are no exclusive waiters, even if
* we already own it shared. */
else if (!resource->NumberOfExclusiveWaiters)
{
entry->OwnerCount++;
resource->ActiveEntries++;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
if (!wait)
{
KeReleaseSpinLock( &resource->SpinLock, irql );
return FALSE;
}
if (!resource->SharedWaiters)
{
resource->SharedWaiters = heap_alloc( sizeof(*resource->SharedWaiters) );
KeInitializeSemaphore( resource->SharedWaiters, 0, INT_MAX );
}
resource->NumberOfSharedWaiters++;
KeReleaseSpinLock( &resource->SpinLock, irql );
KeWaitForSingleObject( resource->SharedWaiters, Executive, KernelMode, FALSE, NULL );
KeAcquireSpinLock( &resource->SpinLock, &irql );
entry->OwnerCount++;
resource->ActiveEntries++;
resource->NumberOfSharedWaiters--;
KeReleaseSpinLock( &resource->SpinLock, irql );
return TRUE;
}
/***********************************************************************
* ExReleaseResourceForThreadLite (NTOSKRNL.EXE.@)
*/
void WINAPI ExReleaseResourceForThreadLite( ERESOURCE *resource, ERESOURCE_THREAD thread )
{
OWNER_ENTRY *entry;
KIRQL irql;
TRACE("resource %p, thread %#lx.\n", resource, thread);
KeAcquireSpinLock( &resource->SpinLock, &irql );
if (resource->Flag & ResourceOwnedExclusive)
{
if (resource->OwnerEntry.OwnerThread == thread)
{
if (!--resource->ActiveEntries)
{
resource->OwnerEntry.OwnerThread = 0;
resource->Flag &= ~ResourceOwnedExclusive;
}
}
else
{
ERR("Trying to release %p for thread %#lx, but resource is exclusively owned by %#lx.\n",
resource, thread, resource->OwnerEntry.OwnerThread);
return;
}
}
else
{
entry = resource_get_shared_entry( resource, thread );
if (entry->OwnerCount)
{
entry->OwnerCount--;
resource->ActiveEntries--;
}
else
{
ERR("Trying to release %p for thread %#lx, but resource is not owned by that thread.\n", resource, thread);
return;
}
}
if (!resource->ActiveEntries)
{
if (resource->NumberOfExclusiveWaiters)
{
KeSetEvent( resource->ExclusiveWaiters, IO_NO_INCREMENT, FALSE );
}
else if (resource->NumberOfSharedWaiters)
{
KeReleaseSemaphore( resource->SharedWaiters, IO_NO_INCREMENT,
resource->NumberOfSharedWaiters, FALSE );
}
}
KeReleaseSpinLock( &resource->SpinLock, irql );
}
/***********************************************************************
* ExReleaseResourceLite (NTOSKRNL.EXE.@)
*/
DEFINE_FASTCALL1_WRAPPER( ExReleaseResourceLite )
void FASTCALL ExReleaseResourceLite( ERESOURCE *resource )
{
ExReleaseResourceForThreadLite( resource, (ERESOURCE_THREAD)KeGetCurrentThread() );
}
/***********************************************************************
* ExGetExclusiveWaiterCount (NTOSKRNL.EXE.@)
*/
ULONG WINAPI ExGetExclusiveWaiterCount( ERESOURCE *resource )
{
ULONG count;
KIRQL irql;
TRACE("resource %p.\n", resource);
KeAcquireSpinLock( &resource->SpinLock, &irql );
count = resource->NumberOfExclusiveWaiters;
KeReleaseSpinLock( &resource->SpinLock, irql );
return count;
}
/***********************************************************************
* ExGetSharedWaiterCount (NTOSKRNL.EXE.@)
*/
ULONG WINAPI ExGetSharedWaiterCount( ERESOURCE *resource )
{
ULONG count;
KIRQL irql;
TRACE("resource %p.\n", resource);
KeAcquireSpinLock( &resource->SpinLock, &irql );
count = resource->NumberOfSharedWaiters;
KeReleaseSpinLock( &resource->SpinLock, irql );
return count;
}
/***********************************************************************
* ExIsResourceAcquiredExclusiveLite (NTOSKRNL.EXE.@)
*/
BOOLEAN WINAPI ExIsResourceAcquiredExclusiveLite( ERESOURCE *resource )
{
BOOLEAN ret;
KIRQL irql;
TRACE("resource %p.\n", resource);
KeAcquireSpinLock( &resource->SpinLock, &irql );
ret = (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread());
KeReleaseSpinLock( &resource->SpinLock, irql );
return ret;
}
/***********************************************************************
* ExIsResourceAcquiredSharedLite (NTOSKRNL.EXE.@)
*/
ULONG WINAPI ExIsResourceAcquiredSharedLite( ERESOURCE *resource )
{
ULONG ret;
KIRQL irql;
TRACE("resource %p.\n", resource);
KeAcquireSpinLock( &resource->SpinLock, &irql );
if (resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)KeGetCurrentThread())
ret = resource->ActiveEntries;
else
{
OWNER_ENTRY *entry = resource_get_shared_entry( resource, (ERESOURCE_THREAD)KeGetCurrentThread() );
ret = entry->OwnerCount;
}
KeReleaseSpinLock( &resource->SpinLock, irql );
return ret;
}