/*++ Copyright (c) 2000 Microsoft Corporation Module Name: safelock.c Abstract: Implementation of the safe lock library - a set of thin wrappers around critical section and resource routines that ensures proper lock ordering. Debug spew is generated when locks are acquired out of order. --*/ #include #include #ifdef DBG // // Uncomment the #define below to assert on locks acquired out // of order. Otherwise, only debug spew is generated. // #define SAFE_LOCK_ASSERT #define MAX_SAFE_LOCK_ENTRIES 64 typedef struct _SAFE_LOCK_ENTRY { DWORD Enum; DWORD Count; } SAFE_LOCK_ENTRY; typedef struct _SAFE_LOCK_STACK { DWORD Top; SAFE_LOCK_ENTRY Entries[MAX_SAFE_LOCK_ENTRIES]; } SAFE_LOCK_STACK, *PSAFE_LOCK_STACK; DWORD SafeLockThreadState = TLS_OUT_OF_INDEXES; /////////////////////////////////////////////////////////////////////////////// // // Helper routines // /////////////////////////////////////////////////////////////////////////////// NTSTATUS SafeLockInit() /*++ Routine Description: Called by the user of the safelock code at startup time, once per process initialization. Parameters: None Returns: STATUS_INSUFFICIENT_RESOURCES TlsAlloc failed STATUS_SUCCESS Otherwise --*/ { if ( SafeLockThreadState == TLS_OUT_OF_INDEXES ) { SafeLockThreadState = TlsAlloc(); if ( SafeLockThreadState == TLS_OUT_OF_INDEXES ) { return STATUS_INSUFFICIENT_RESOURCES; } } return STATUS_SUCCESS; } VOID TrackLockEnter( DWORD Enum ) /*++ Routine Description: Used to insert tracking information about a lock into the stack Parameters: Enum ordinal number associated with the lock Returns: Nothing, but will assert if not happy --*/ { PSAFE_LOCK_STACK Stack; DWORD Index; // // First see if the space for the stack has been allocated // Stack = ( SAFE_LOCK_STACK * )TlsGetValue( SafeLockThreadState ); if ( Stack == NULL ) { Stack = ( PSAFE_LOCK_STACK )LocalAlloc( 0, sizeof( SAFE_LOCK_STACK )); if ( Stack == NULL ) { // // Got no better way of dealing with this error here // DbgPrint( "Out of memory allocating lock tracking stack\n" ); #ifdef SAFE_LOCK_ASSERT ASSERT( FALSE ); #endif return; } ZeroMemory( Stack, sizeof( SAFE_LOCK_STACK )); TlsSetValue( SafeLockThreadState, Stack ); } if ( Stack->Top >= MAX_SAFE_LOCK_ENTRIES ) { DbgPrint( "Lock tracking stack is full\n" ); #ifdef SAFE_LOCK_ASSERT ASSERT( FALSE ); #endif return; } if ( Stack->Top == 0 || Enum > Stack->Entries[Stack->Top-1].Enum ) { // // Lock acquired in order; no further checks are necessary // Stack->Entries[Stack->Top].Enum = Enum; Stack->Entries[Stack->Top].Count = 1; Stack->Top += 1; } else { // // See if this lock has been acquired already // for ( Index = 0; Index < Stack->Top; Index++ ) { if ( Stack->Entries[Index].Enum == Enum ) { Stack->Entries[Index].Count += 1; break; } } if ( Index == Stack->Top ) { CHAR Buffer[128]; sprintf( Buffer, "Lock %d acquired out of order: dt %p SAFE_LOCK_STACK\n", Enum, Stack ); DbgPrint( Buffer ); #ifdef SAFE_LOCK_ASSERT ASSERT( FALSE ); #endif // // To keep the stack consistent, insert the new item // as if it was acquired in proper order // for ( Index = 0; Index < Stack->Top; Index++ ) { if ( Enum < Stack->Entries[Index].Enum ) { MoveMemory( &Stack->Entries[Index+1], &Stack->Entries[Index], sizeof( SAFE_LOCK_ENTRY ) * ( Stack->Top - Index )); break; } } Stack->Entries[Index].Enum = Enum; Stack->Entries[Index].Count = 1; Stack->Top += 1; } } return; } VOID TrackLockLeave( DWORD Enum ) /*++ Routine Description: Used to remove tracking information about a lock from the stack Parameters: Enum ordinal number associated with the lock Returns: Nothing, but will assert if not happy --*/ { PSAFE_LOCK_STACK Stack; DWORD Index; Stack = ( SAFE_LOCK_STACK * )TlsGetValue( SafeLockThreadState ); if ( Stack == NULL ) { DbgPrint( "No lock tracking information available for this thread\n" ); #ifdef SAFE_LOCK_ASSERT ASSERT( FALSE ); #endif return; } if ( Stack->Top == 0 ) { DbgPrint( "Lock tracking stack is empty\n" ); #ifdef SAFE_LOCK_ASSERT ASSERT( FALSE ); #endif return; } // // See if this lock has been acquired already // for ( Index = 0; Index < Stack->Top; Index++ ) { if ( Stack->Entries[Index].Enum == Enum ) { Stack->Entries[Index].Count -= 1; break; } } if ( Index == Stack->Top ) { CHAR Buffer[128]; sprintf( Buffer, "Leaving a lock %d that has not been acquired: dt %p SAFE_LOCK_STACK\n", Enum, Stack ); DbgPrint( Buffer ); #ifdef SAFE_LOCK_ASSERT ASSERT( FALSE ); #endif } else if ( Stack->Entries[Index].Count == 0 ) { // // Compact the stack // Stack->Top -= 1; MoveMemory( &Stack->Entries[Index], &Stack->Entries[Index+1], sizeof( SAFE_LOCK_ENTRY ) * ( Stack->Top - Index )); } if ( Stack->Top == 0 ) { LocalFree( Stack ); TlsSetValue( SafeLockThreadState, NULL ); } return; } /////////////////////////////////////////////////////////////////////////////// // // RTL_CRITICAL_SECTION wrappers // /////////////////////////////////////////////////////////////////////////////// NTSTATUS SafeEnterCriticalSection( PSAFE_CRITICAL_SECTION CriticalSection ) /*++ Routine Description: Debug wrapper around RtlEnterCriticalSection. Asserts if it is not happy. Arguments: CriticalSection address of a SAFE_CRITICAL_SECTION to enter Returns: See RtlEnterCriticalSection --*/ { NTSTATUS Status; TrackLockEnter( CriticalSection->Enum ); Status = RtlEnterCriticalSection( &CriticalSection->CriticalSection ); return Status; } NTSTATUS SafeLeaveCriticalSection( PSAFE_CRITICAL_SECTION CriticalSection ) /*++ Routine Description: Debug wrapper around RtlLeaveCriticalSection that ensures proper ordering of locks. Asserts if it is not happy. Arguments: CriticalSection address of a SAFE_CRITICAL_SECTION to leave Returns: See RtlLeaveCriticalSection --*/ { NTSTATUS Status; TrackLockLeave( CriticalSection->Enum ); Status = RtlLeaveCriticalSection( &CriticalSection->CriticalSection ); return Status; } BOOLEAN SafeTryEnterCriticalSection( PSAFE_CRITICAL_SECTION CriticalSection ) /*++ Routine Description: Debug wrapper around RtlTryEnterCriticalSection that ensures proper ordering of locks. Asserts if it is not happy. Arguments: CriticalSection address of a SAFE_CRITICAL_SECTION to enter Returns: See RtlTryEnterCriticalSection --*/ { BOOLEAN Result; TrackLockEnter( CriticalSection->Enum ); Result = RtlTryEnterCriticalSection( &CriticalSection->CriticalSection ); if ( !Result ) { TrackLockLeave( CriticalSection->Enum ); } return Result; } NTSTATUS SafeInitializeCriticalSection( PSAFE_CRITICAL_SECTION CriticalSection, DWORD Enum ) /*++ Routine Description: Debug wrapper around RtlInitializeCriticalSection. Arguments: CriticalSection address of a SAFE_CRITICAL_SECTION to initialize Enum ordinal number associated with the critical section Returns: See RtlInitializeCriticalSection --*/ { NTSTATUS Status; CriticalSection->Enum = Enum; Status = RtlInitializeCriticalSection( &CriticalSection->CriticalSection ); return Status; } NTSTATUS SafeInitializeCriticalSectionAndSpinCount( PSAFE_CRITICAL_SECTION CriticalSection, ULONG SpinCount, DWORD Enum ) /*++ Routine Description: Debug wrapper around RtlInitializeCriticalSectionAndSpinCount. Arguments: CriticalSection address of a SAFE_CRITICAL_SECTION to initialize SpinCount spin count Enum ordinal number associated with the critical section Returns: See RtlInitializeCriticalSectionAndSpinCount --*/ { NTSTATUS Status; CriticalSection->Enum = Enum; Status = RtlInitializeCriticalSectionAndSpinCount( &CriticalSection->CriticalSection, SpinCount ); return Status; } ULONG SafeSetCriticalSectionSpinCount( PSAFE_CRITICAL_SECTION CriticalSection, ULONG SpinCount ) /*++ Routine Description: Debug wrapper around RtlSetCriticalSectionSpinCount. Arguments: CriticalSection address of a SAFE_CRITICAL_SECTION to modify SpinCount see the definition of RtlSetCriticalSectionSpinCount Returns: See RtlSetCriticalSectionSpinCount --*/ { ULONG Result; Result = RtlSetCriticalSectionSpinCount( &CriticalSection->CriticalSection, SpinCount ); return Result; } NTSTATUS SafeDeleteCriticalSection( PSAFE_CRITICAL_SECTION CriticalSection ) /*++ Routine Description: Debug wrapper around RtlDeleteCriticalSection. Arguments: CriticalSection address of a SAFE_CRITICAL_SECTION to delete Returns: See RtlDeleteCriticalSection --*/ { NTSTATUS Status; Status = RtlDeleteCriticalSection( &CriticalSection->CriticalSection ); return Status; } /////////////////////////////////////////////////////////////////////////////// // // RTL_RESOURCE wrappers // /////////////////////////////////////////////////////////////////////////////// VOID SafeInitializeResource( PSAFE_RESOURCE Resource, DWORD Enum ) /*++ Routine Description: Debug wrapper around RtlInitializeResource. Arguments: Resource address of a SAFE_RESOURCE to initialize Enum ordinal number associated with the resource Returns: See RtlInitializeResource --*/ { Resource->Enum = Enum; RtlInitializeResource( &Resource->Resource ); return; } BOOLEAN SafeAcquireResourceShared( PSAFE_RESOURCE Resource, BOOLEAN Wait ) /*++ Routine Description: Debug wrapper around RtlAcquireResourceShared that ensures proper ordering of locks. Asserts if it is not happy. Arguments: Resource address of a SAFE_RESOURCE to enter Wait see definition of RtlAcquireResourceShared Returns: See RtlAcquireResourceShared --*/ { BOOLEAN Result; TrackLockEnter( Resource->Enum ); Result = RtlAcquireResourceShared( &Resource->Resource, Wait ); if ( !Result ) { TrackLockLeave( Resource->Enum ); } return Result; } BOOLEAN SafeAcquireResourceExclusive( PSAFE_RESOURCE Resource, BOOLEAN Wait ) /*++ Routine Description: Debug wrapper around RtlAcquireResourceExclusive that ensures proper ordering of locks. Asserts if it is not happy. Arguments: Resource address of a SAFE_RESOURCE to enter Wait see definition of RtlAcquireResourceExclusive Returns: See RtlAcquireResourceExclusive --*/ { BOOLEAN Result; TrackLockEnter( Resource->Enum ); Result = RtlAcquireResourceExclusive( &Resource->Resource, Wait ); if ( !Result ) { TrackLockLeave( Resource->Enum ); } return Result; } VOID SafeReleaseResource( PSAFE_RESOURCE Resource ) /*++ Routine Description: Debug wrapper around RtlReleaseResource that ensures proper ordering of locks. Asserts if it is not happy. Arguments: Resource address of a SAFE_RESOURCE to release Returns: See RtlReleaseResource --*/ { TrackLockLeave( Resource->Enum ); RtlReleaseResource( &Resource->Resource ); return; } VOID SafeConvertSharedToExclusive( PSAFE_RESOURCE Resource ) /*++ Routine Description: Debug wrapper around RtlConvertSharedToExclusive. Arguments: Resource address of a SAFE_RESOURCE to convert Returns: See RtlConvertSharedToExclusive --*/ { RtlConvertSharedToExclusive( &Resource->Resource ); return; } VOID SafeConvertExclusiveToShared( PSAFE_RESOURCE Resource ) /*++ Routine Description: Debug wrapper around RtlConvertExclusiveToShared. Arguments: Resource address of a SAFE_RESOURCE to convert Returns: See RtlConvertExclusiveToShared --*/ { RtlConvertExclusiveToShared( &Resource->Resource ); return; } VOID SafeDeleteResource ( PSAFE_RESOURCE Resource ) /*++ Routine Description: Debug wrapper around RtlDeleteResource. Arguments: Resource address of a SAFE_RESOURCE to delete Returns: See RtlDeleteResource --*/ { RtlDeleteResource( &Resource->Resource ); return; } #endif