diff --git a/reactos/include/rosrtl/recmutex.h b/reactos/include/rosrtl/recmutex.h new file mode 100644 index 00000000000..a170d4aed18 --- /dev/null +++ b/reactos/include/rosrtl/recmutex.h @@ -0,0 +1,27 @@ +#ifndef _ROSRTL_RECMUTEX_H +#define _ROSRTL_RECMUTEX_H + +typedef struct _RECURSIVE_MUTEX { + /* Lock. */ + FAST_MUTEX Mutex; + /* Number of times this object was locked */ + UINT LockCount; + /* CurrentThread */ + PVOID CurrentThread; + /* Notification event which signals that another thread can take over */ + KEVENT StateLockedEvent; + /* IRQL from spin lock */ + KIRQL OldIrql; + /* Is Locked */ + BOOLEAN Locked; + /* Is reader or writer phase */ + BOOLEAN Writer; + /* Spin lock needed for */ + KSPIN_LOCK SpinLock; +} RECURSIVE_MUTEX, *PRECURSIVE_MUTEX; + +extern VOID RecursiveMutexInit( PRECURSIVE_MUTEX RecMutex ); +extern UINT RecursiveMutexEnter( PRECURSIVE_MUTEX RecMutex, BOOL ToRead ); +extern VOID RecursiveMutexLeave( PRECURSIVE_MUTEX RecMutex ); + +#endif/*_ROSRTL_RECMUTEX_H*/ diff --git a/reactos/lib/rosrtl/makefile b/reactos/lib/rosrtl/makefile index 79bbb9a049e..6fa7b38c5ef 100644 --- a/reactos/lib/rosrtl/makefile +++ b/reactos/lib/rosrtl/makefile @@ -1,4 +1,4 @@ -# $Id: makefile,v 1.16 2004/09/15 18:57:01 weiden Exp $ +# $Id: makefile,v 1.17 2004/09/25 21:32:17 arty Exp $ PATH_TO_TOP = ../.. @@ -32,6 +32,8 @@ MISC_OBJECTS = \ FILE_OBJECTS = \ file/sparse.o +RECMUTEX_OBJECTS = recmutex/recmutex.o + include $(PATH_TO_TOP)/config include makefile.$(ARCH) @@ -43,8 +45,8 @@ TARGET_NAME = rosrtl TARGET_CFLAGS = -D__USE_W32API -Wall -Werror TARGET_OBJECTS = $(THREAD_OBJECTS) $(MISC_OBJECTS) $(STRING_OBJECTS) \ - $(REGISTRY_OBJECTS) $(FILE_OBJECTS) - + $(REGISTRY_OBJECTS) $(FILE_OBJECTS) $(RECMUTEX_OBJECTS) + DEP_OBJECTS = $(TARGET_OBJECTS) include $(PATH_TO_TOP)/rules.mak diff --git a/reactos/lib/rosrtl/recmutex/recmutex.c b/reactos/lib/rosrtl/recmutex/recmutex.c new file mode 100644 index 00000000000..a07d25cf4e9 --- /dev/null +++ b/reactos/lib/rosrtl/recmutex/recmutex.c @@ -0,0 +1,84 @@ +#define NTOS_MODE_USER +#include +#include + +VOID RecursiveMutexInit( PRECURSIVE_MUTEX RecMutex ) { + RtlZeroMemory( RecMutex, sizeof(*RecMutex) ); + KeInitializeSpinLock( &RecMutex->SpinLock ); + ExInitializeFastMutex( &RecMutex->Mutex ); + KeInitializeEvent( &RecMutex->StateLockedEvent, + NotificationEvent, FALSE ); +} + +/* NOTE: When we leave, the FAST_MUTEX must have been released. The result + * is that we always exit in the same irql as entering */ +UINT RecursiveMutexEnter( PRECURSIVE_MUTEX RecMutex, BOOL ToWrite ) { + NTSTATUS Status = STATUS_SUCCESS; + PVOID CurrentThread = KeGetCurrentThread(); + + /* Wait for the previous user to unlock the RecMutex state. There might be + * multiple waiters waiting to change the state. We need to check each + * time we get the event whether somebody still has the state locked */ + + if( !RecMutex ) return FALSE; + + if( CurrentThread == RecMutex->CurrentThread || + (!ToWrite && !RecMutex->Writer) ) { + RecMutex->LockCount++; + return TRUE; + } + + if( KeGetCurrentIrql() == PASSIVE_LEVEL ) { + ExAcquireFastMutex( &RecMutex->Mutex ); + RecMutex->OldIrql = PASSIVE_LEVEL; + while( RecMutex->Locked ) { + ExReleaseFastMutex( &RecMutex->Mutex ); + Status = KeWaitForSingleObject( &RecMutex->StateLockedEvent, + UserRequest, + KernelMode, + FALSE, + NULL ); + ExAcquireFastMutex( &RecMutex->Mutex ); + if( Status == STATUS_SUCCESS ) break; + } + RecMutex->Locked = TRUE; + RecMutex->Writer = ToWrite; + RecMutex->CurrentThread = CurrentThread; + RecMutex->LockCount++; + ExReleaseFastMutex( &RecMutex->Mutex ); + } else { + KeAcquireSpinLock( &RecMutex->SpinLock, &RecMutex->OldIrql ); + RecMutex->Locked = TRUE; + RecMutex->Writer = ToWrite; + RecMutex->CurrentThread = CurrentThread; + RecMutex->LockCount++; + } + + return TRUE; +} + +VOID RecursiveMutexLeave( PRECURSIVE_MUTEX RecMutex ) { + if( RecMutex->LockCount == 0 ) { + return; + } else + RecMutex->LockCount--; + + if( !RecMutex->LockCount ) { + RecMutex->CurrentThread = NULL; + if( RecMutex->OldIrql == PASSIVE_LEVEL ) { + ExAcquireFastMutex( &RecMutex->Mutex ); + RecMutex->Locked = FALSE; + RecMutex->Writer = FALSE; + ExReleaseFastMutex( &RecMutex->Mutex ); + } else { + RecMutex->Locked = FALSE; + RecMutex->Writer = FALSE; + KeReleaseSpinLock( &RecMutex->SpinLock, RecMutex->OldIrql ); + } + + RecMutex->OldIrql = PASSIVE_LEVEL; + KePulseEvent( &RecMutex->StateLockedEvent, IO_NETWORK_INCREMENT, + FALSE ); + } +} +