831 lines
22 KiB
C
831 lines
22 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
lock.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements functions for the LAN Manager server FSP's
|
|||
|
lock package. This package began as a modification and streamlining
|
|||
|
of the executive resource package -- it allowed recursive
|
|||
|
acquisition, but didn't provide shared locks. Later, debugging
|
|||
|
support in the form of level checking was added.
|
|||
|
|
|||
|
Coming full circle, the package now serves as a wrapper around the
|
|||
|
real resource package. It simply provides debugging support. The
|
|||
|
reasons for reverting to using resources include:
|
|||
|
|
|||
|
1) The resource package now supports recursive acquisition.
|
|||
|
|
|||
|
2) There are a couple of places in the server where shared access
|
|||
|
is desirable.
|
|||
|
|
|||
|
3) The resource package has a "no-wait" option that disables waiting
|
|||
|
for a lock when someone else owns it. This feature is useful to
|
|||
|
the server FSD.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Chuck Lenzmeier (chuckl) 29-Nov-1989
|
|||
|
A modification of Gary Kimura's resource.c. This version does
|
|||
|
not support shared ownership, only exclusive ownership. Support
|
|||
|
for recursive ownership has been added.
|
|||
|
David Treadwell (davidtr)
|
|||
|
|
|||
|
Chuck Lenzmeier (chuckl) 5-Apr-1991
|
|||
|
Revert to using resource package.
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only, LAN Manager server FSP and FSD.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#include "lock.tmh"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
#if SRVDBG_LOCK
|
|||
|
|
|||
|
#define BugCheckFileId SRV_FILE_LOCK
|
|||
|
|
|||
|
//
|
|||
|
// *** This entire module is conditionalized away when SRVDBG_LOCK is off.
|
|||
|
//
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text( PAGE, SrvInitializeLock )
|
|||
|
#pragma alloc_text( PAGE, SrvDeleteLock )
|
|||
|
#pragma alloc_text( PAGE, SrvReleaseLock )
|
|||
|
#endif
|
|||
|
#if 0
|
|||
|
NOT PAGEABLE -- SrvAcquireLock
|
|||
|
NOT PAGEABLE -- SrvCheckListIntegrity
|
|||
|
NOT PAGEABLE -- SrvIsEntryInList
|
|||
|
NOT PAGEABLE -- SrvIsEntryNotInList
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Lock Level Semantics:
|
|||
|
//
|
|||
|
// Lock levels are used for server lock debugging as an aid in
|
|||
|
// preventing deadlocks. A deadlock may occur if two (or more) threads
|
|||
|
// attempt to acquire two (or more) locks in a different order. For
|
|||
|
// example, suppose that there are two threads, 0 and 1, and two locks,
|
|||
|
// A and B. Then suppose the following happens:
|
|||
|
//
|
|||
|
// - thread 0 acquires lock A
|
|||
|
// - thread 1 acquires lock B
|
|||
|
// - thread 0 attempts to acquire lock B, gets blocked
|
|||
|
// - thread 1 attempts to acquire lock A, gets blocked.
|
|||
|
//
|
|||
|
// This results in deadlock, where all threads are blocked and cannot
|
|||
|
// become unblocked. To prevent it, all threads must acquire locks in
|
|||
|
// the same order. In the above example, if we had the rule that lock A
|
|||
|
// must be acquired before lock B, then thread 1 would have blocked
|
|||
|
// while attempting to acquire lock A, but thread 0 would have been able
|
|||
|
// to acquire lock B and completed its work.
|
|||
|
//
|
|||
|
// This rule is implemented generally in the server by lock levels. The
|
|||
|
// lock levels are set up such that lower-level locks are acquired
|
|||
|
// first, then higher level locks. An attempt to acquire locks out of
|
|||
|
// order will be caught during debugging. The rules are as follows:
|
|||
|
//
|
|||
|
// - A lock's level is assigned during initialization.
|
|||
|
//
|
|||
|
// - A thread may acquire any lock with a level greater than the level
|
|||
|
// of the highest held exclusive lock, but an attempt to acquire a
|
|||
|
// lock with a level equal to less than the highest lock will fail.
|
|||
|
// Note that full level checking is _not_ done for shared locks,
|
|||
|
// because of the difficulty of trying to retain information about the
|
|||
|
// number of times multiple threads have obtained a given lock for
|
|||
|
// shared access.
|
|||
|
//
|
|||
|
// - Recursive acquisitions of locks are legal, even if there are
|
|||
|
// intervening lock acquisitions. For example, this is legal:
|
|||
|
// thread acquires lock A
|
|||
|
// thread acquires lock B
|
|||
|
// thread recursively acquires lock A
|
|||
|
//
|
|||
|
// - Locks may be released in any order.
|
|||
|
//
|
|||
|
// Lock debugging is active only when debugging is turned on.
|
|||
|
//
|
|||
|
|
|||
|
#define HAS_TEB(_teb) ((BOOLEAN)(((ULONG)(_teb) <= MM_HIGHEST_USER_ADDRESS) ? FALSE : MmIsNonPagedSystemAddressValid(_teb)))
|
|||
|
|
|||
|
#define SrvCurrentTeb( ) ((PTEB)(KeGetCurrentThread( )->Teb))
|
|||
|
|
|||
|
#define SrvTebLockList( ) \
|
|||
|
((PLIST_ENTRY)&(SrvCurrentTeb( )->UserReserved[SRV_TEB_LOCK_LIST]))
|
|||
|
|
|||
|
#define SrvThreadLockAddress( ) \
|
|||
|
( IsListEmpty( SrvTebLockList( ) ) ? 0 : CONTAINING_RECORD( \
|
|||
|
SrvTebLockList( )->Flink, \
|
|||
|
SRV_LOCK, \
|
|||
|
Header.ThreadListEntry \
|
|||
|
) )
|
|||
|
|
|||
|
#define SrvThreadLockLevel( ) \
|
|||
|
( IsListEmpty( SrvTebLockList( ) ) ? 0 : CONTAINING_RECORD( \
|
|||
|
SrvTebLockList( )->Flink, \
|
|||
|
SRV_LOCK, \
|
|||
|
Header.ThreadListEntry \
|
|||
|
)->Header.LockLevel )
|
|||
|
|
|||
|
#define SrvThreadLockName( ) \
|
|||
|
( IsListEmpty( SrvTebLockList( ) ) ? "none" : CONTAINING_RECORD( \
|
|||
|
SrvTebLockList( )->Flink, \
|
|||
|
SRV_LOCK, \
|
|||
|
Header.ThreadListEntry \
|
|||
|
)->Header.LockName )
|
|||
|
|
|||
|
KSPIN_LOCK LockSpinLock = {0};
|
|||
|
BOOLEAN LockSpinLockInitialized = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Forward declarations.
|
|||
|
//
|
|||
|
|
|||
|
#define MAX_LOCKS_HELD 15
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SrvInitializeLock(
|
|||
|
IN PSRV_LOCK Lock,
|
|||
|
IN ULONG LockLevel,
|
|||
|
IN PSZ LockName
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine initializes the input lock variable.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Lock - Supplies the lock variable being initialized
|
|||
|
|
|||
|
LockLevel - Supplies the level of the lock
|
|||
|
|
|||
|
LockName - Supplies the name of the lock
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
if ( !LockSpinLockInitialized ) {
|
|||
|
LockSpinLockInitialized = TRUE;
|
|||
|
INITIALIZE_SPIN_LOCK( LockSpinLock );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the executive resource.
|
|||
|
//
|
|||
|
|
|||
|
ExInitializeResource( &Lock->Resource );
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the lock level. This is used to determine whether a
|
|||
|
// thread may acquire the lock. Save the lock name.
|
|||
|
//
|
|||
|
|
|||
|
LOCK_LEVEL( Lock ) = LockLevel;
|
|||
|
|
|||
|
LOCK_NAME( Lock ) = LockName;
|
|||
|
|
|||
|
IF_DEBUG(LOCKS) {
|
|||
|
SrvPrint3( "Initialized %s(%lx, L%lx)\n",
|
|||
|
LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ) );
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // SrvInitializeLock
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SrvDeleteLock (
|
|||
|
IN PSRV_LOCK Lock
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine deletes (i.e., uninitializes) a lock variable.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Lock - Supplies the lock variable being deleted
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the lock is unowned.
|
|||
|
//
|
|||
|
|
|||
|
if ( LOCK_NUMBER_OF_ACTIVE( Lock ) != 0 ) {
|
|||
|
|
|||
|
IF_DEBUG(ERRORS) {
|
|||
|
SrvPrint1( "SrvDeleteLock: Thread %d\n", KeGetCurrentThread( ) );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This internal error bugchecks the system.
|
|||
|
//
|
|||
|
|
|||
|
INTERNAL_ERROR(
|
|||
|
ERROR_LEVEL_IMPOSSIBLE,
|
|||
|
"SrvDeleteLock: Attempt to delete owned lock %s(%lx)",
|
|||
|
LOCK_NAME( Lock ),
|
|||
|
Lock
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Delete the resource.
|
|||
|
//
|
|||
|
|
|||
|
ExDeleteResource( &Lock->Resource );
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // SrvDeleteLock
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SrvAcquireLock(
|
|||
|
IN PSRV_LOCK Lock,
|
|||
|
IN BOOLEAN Wait,
|
|||
|
IN BOOLEAN Exclusive
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The routine acquires a lock.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Lock - Supplies the lock to acquire
|
|||
|
|
|||
|
Wait - Indicates whether the caller wants to wait for the resource
|
|||
|
if it is already owned
|
|||
|
|
|||
|
Exclusive - Indicates whether exlusive or shared access is desired
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - Indicates whether the lock was acquired. This will always
|
|||
|
be TRUE if Wait is TRUE.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PKTHREAD currentThread;
|
|||
|
PTEB currentTeb;
|
|||
|
BOOLEAN hasTeb;
|
|||
|
ULONG threadLockLevel;
|
|||
|
BOOLEAN lockAcquired;
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
currentThread = (PKTHREAD)ExGetCurrentResourceThread( );
|
|||
|
currentTeb = SrvCurrentTeb( );
|
|||
|
hasTeb = HAS_TEB(currentTeb);
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that we are at an IRQL lower than DISPATCH_LEVEL, if
|
|||
|
// Wait is TRUE. We cannot wait to acquire a lock at that IRQL or
|
|||
|
// above.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( !Wait || (KeGetCurrentIrql( ) < DISPATCH_LEVEL) );
|
|||
|
|
|||
|
//
|
|||
|
// If this thread does not have a nonpaged TEB, do not do lock-level
|
|||
|
// debugging. (We might be at DPC level, so we can't take page
|
|||
|
// faults.)
|
|||
|
//
|
|||
|
|
|||
|
if ( hasTeb ) {
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that this thread has been initialized for lock
|
|||
|
// debugging. If not, initialize it.
|
|||
|
//
|
|||
|
|
|||
|
ACQUIRE_SPIN_LOCK( LockSpinLock, &oldIrql );
|
|||
|
if ( (ULONG)currentTeb->UserReserved[SRV_TEB_LOCK_INIT] !=
|
|||
|
0xbabababa ) {
|
|||
|
PLIST_ENTRY tebLockList = SrvTebLockList( );
|
|||
|
InitializeListHead( tebLockList );
|
|||
|
currentTeb->UserReserved[SRV_TEB_LOCK_INIT] = (PVOID)0xbabababa;
|
|||
|
}
|
|||
|
RELEASE_SPIN_LOCK( LockSpinLock, oldIrql );
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the list of locks in the TEB is consistent.
|
|||
|
//
|
|||
|
|
|||
|
SrvCheckListIntegrity( SrvTebLockList( ), MAX_LOCKS_HELD );
|
|||
|
|
|||
|
//
|
|||
|
// The "lock level" of this thread is the highest level of the
|
|||
|
// locks currently held exclusively. If this thread holds no
|
|||
|
// locks, the lock level of the thread is 0 and it can acquire
|
|||
|
// any lock.
|
|||
|
//
|
|||
|
|
|||
|
threadLockLevel = SrvThreadLockLevel( );
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the lock the thread is attempting to acquire
|
|||
|
// has a higher level than the last-acquired exclusive lock.
|
|||
|
// Note that a recursive exclusive acquisition of a lock should
|
|||
|
// succeed, even if a different, higher-level lock has been
|
|||
|
// acquired since the lock was originally acquired. Shared
|
|||
|
// acquisition of a lock that is already held exclusively must
|
|||
|
// fail.
|
|||
|
//
|
|||
|
// *** We do NOT make this check if the caller isn't going to
|
|||
|
// wait for the lock, because no-wait acquisitions cannot
|
|||
|
// actually induce deadlock. The server FSD does this at
|
|||
|
// DPC level, potentially having interrupted a server FSP
|
|||
|
// thread that holds a higher-level lock.
|
|||
|
//
|
|||
|
|
|||
|
if ( Wait &&
|
|||
|
(LOCK_LEVEL( Lock ) <= threadLockLevel) &&
|
|||
|
(!Exclusive ||
|
|||
|
!ExIsResourceAcquiredExclusive( &Lock->Resource )) ) {
|
|||
|
|
|||
|
SrvPrint4( "Thread %lx, last lock %s(%lx, L%lx) attempted to ",
|
|||
|
currentThread,
|
|||
|
SrvThreadLockName( ), SrvThreadLockAddress( ),
|
|||
|
threadLockLevel );
|
|||
|
SrvPrint4( "acquire %s(%lx, L%lx) for %s access.\n",
|
|||
|
LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
|
|||
|
Exclusive ? "exclusive" : "shared" );
|
|||
|
DbgBreakPoint( );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Acquire the lock.
|
|||
|
//
|
|||
|
|
|||
|
if ( Exclusive ) {
|
|||
|
lockAcquired = ExAcquireResourceExclusive( &Lock->Resource, Wait );
|
|||
|
} else {
|
|||
|
lockAcquired = ExAcquireResourceShared( &Lock->Resource, Wait );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the lock could not be acquired (Wait == FALSE), print a debug
|
|||
|
// message.
|
|||
|
//
|
|||
|
|
|||
|
if ( !lockAcquired ) {
|
|||
|
|
|||
|
IF_DEBUG(LOCKS) {
|
|||
|
SrvPrint4( "%s(%lx, L%lx) no-wait %s acquistion ",
|
|||
|
LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
|
|||
|
Exclusive ? "exclusive" : "shared" );
|
|||
|
SrvPrint1( "by thread %lx failed\n", currentThread );
|
|||
|
}
|
|||
|
|
|||
|
} else if ( !Exclusive ) {
|
|||
|
|
|||
|
//
|
|||
|
// For shared locks, we don't retain any information about
|
|||
|
// the fact that they're owned by this thread.
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(LOCKS) {
|
|||
|
PSZ name = hasTeb ? SrvThreadLockName( ) : "n/a";
|
|||
|
PVOID address = hasTeb ? SrvThreadLockAddress( ) : 0;
|
|||
|
ULONG level = hasTeb ? threadLockLevel : (ULONG)-1;
|
|||
|
SrvPrint4( "%s(%lx, L%lx) acquired shared by thread %lx, ",
|
|||
|
LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
|
|||
|
currentThread );
|
|||
|
SrvPrint3( "last lock %s(%lx L%lx)\n", name, address, level );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The thread acquired the lock for exclusive access.
|
|||
|
//
|
|||
|
|
|||
|
if ( LOCK_NUMBER_OF_ACTIVE( Lock ) == 1 ) {
|
|||
|
|
|||
|
//
|
|||
|
// This is the first time the thread acquired the lock for
|
|||
|
// exclusive access. Update the thread's lock state.
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(LOCKS) {
|
|||
|
PSZ name = hasTeb ? SrvThreadLockName( ) : "n/a";
|
|||
|
PVOID address = hasTeb ? SrvThreadLockAddress( ) : 0;
|
|||
|
ULONG level = hasTeb ? threadLockLevel : (ULONG)-1;
|
|||
|
SrvPrint4( "%s(%lx, L%lx) acquired exclusive by thread %lx, ",
|
|||
|
LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
|
|||
|
currentThread );
|
|||
|
SrvPrint3( "last lock %s(%lx L%lx)\n", name, address, level );
|
|||
|
}
|
|||
|
|
|||
|
if ( hasTeb ) {
|
|||
|
|
|||
|
//
|
|||
|
// Insert the lock on the thread's list of locks.
|
|||
|
//
|
|||
|
|
|||
|
ExInterlockedInsertHeadList(
|
|||
|
SrvTebLockList( ),
|
|||
|
LOCK_THREAD_LIST( Lock ),
|
|||
|
&LockSpinLock
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// This is a recursive acquisition of the lock.
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(LOCKS) {
|
|||
|
SrvPrint4( "%s(%lx, L%lx) reacquired by thread %lx; ",
|
|||
|
LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
|
|||
|
currentThread );
|
|||
|
SrvPrint1( "count %ld\n", LOCK_NUMBER_OF_ACTIVE( Lock ) );
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return lockAcquired;
|
|||
|
|
|||
|
} // SrvAcquireLock
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SrvReleaseLock(
|
|||
|
IN PSRV_LOCK Lock
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine releases a lock.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Lock - Supplies the lock to release
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PKTHREAD currentThread;
|
|||
|
PTEB currentTeb;
|
|||
|
BOOLEAN hasTeb;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
currentThread = (PKTHREAD)ExGetCurrentResourceThread( );
|
|||
|
currentTeb = SrvCurrentTeb( );
|
|||
|
hasTeb = HAS_TEB(currentTeb);
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the lock is really owned by the current thread.
|
|||
|
//
|
|||
|
|
|||
|
if ( LOCK_NUMBER_OF_ACTIVE( Lock ) == 0 ) {
|
|||
|
|
|||
|
// !!! Should crash server on internal error here.
|
|||
|
|
|||
|
SrvPrint3( "Thread %lx releasing unowned lock %s(%lx)\n",
|
|||
|
currentThread, LOCK_NAME( Lock ), Lock );
|
|||
|
DbgBreakPoint( );
|
|||
|
|
|||
|
} else if ( (Lock->Resource.Flag & ResourceOwnedExclusive) &&
|
|||
|
!ExIsResourceAcquiredExclusive(&Lock->Resource) ) {
|
|||
|
|
|||
|
// !!! Should crash server on internal error here.
|
|||
|
|
|||
|
SrvPrint4( "Thread %lx releasing lock %s(%lx) owned by "
|
|||
|
"thread %lx\n",
|
|||
|
currentThread, LOCK_NAME( Lock ), Lock,
|
|||
|
Lock->Resource.InitialOwnerThreads[0] );
|
|||
|
DbgBreakPoint( );
|
|||
|
|
|||
|
} else if ( !(Lock->Resource.Flag & ResourceOwnedExclusive) ) {
|
|||
|
|
|||
|
//
|
|||
|
// The thread is releasing shared access to the lock.
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(LOCKS) {
|
|||
|
SrvPrint4( "%s(%lx, L%lx) released shared by thread %lx\n",
|
|||
|
LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
|
|||
|
currentThread );
|
|||
|
}
|
|||
|
|
|||
|
} else if ( LOCK_NUMBER_OF_ACTIVE( Lock ) == 1 ) {
|
|||
|
|
|||
|
//
|
|||
|
// The thread is fully releasing exclusive access to the lock.
|
|||
|
//
|
|||
|
|
|||
|
if ( hasTeb ) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove the lock from the list of locks held by this
|
|||
|
// thread.
|
|||
|
//
|
|||
|
|
|||
|
ExInterlockedRemoveHeadList(
|
|||
|
LOCK_THREAD_LIST( Lock )->Blink,
|
|||
|
&LockSpinLock
|
|||
|
);
|
|||
|
LOCK_THREAD_LIST( Lock )->Flink = NULL;
|
|||
|
LOCK_THREAD_LIST( Lock )->Blink = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the list of locks in the TEB is consistent.
|
|||
|
//
|
|||
|
|
|||
|
SrvCheckListIntegrity( SrvTebLockList( ), MAX_LOCKS_HELD );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IF_DEBUG(LOCKS) {
|
|||
|
PSZ name = hasTeb ? SrvThreadLockName( ) : "n/a";
|
|||
|
PVOID address = hasTeb ? SrvThreadLockAddress( ) : 0;
|
|||
|
ULONG level = hasTeb ? SrvThreadLockLevel( ) : (ULONG)-1;
|
|||
|
SrvPrint4( "%s(%lx, L%lx) released by thread %lx, ",
|
|||
|
LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
|
|||
|
currentThread );
|
|||
|
SrvPrint3( "new last lock %s(%lx L%lx)\n", name, address, level );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The thread is partially releasing exclusive access to the
|
|||
|
// lock.
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(LOCKS) {
|
|||
|
SrvPrint4( "%s(%lx, L%lx) semireleased by thread %lx; ",
|
|||
|
LOCK_NAME( Lock ), Lock, LOCK_LEVEL( Lock ),
|
|||
|
currentThread );
|
|||
|
SrvPrint1( "new count %ld\n", LOCK_NUMBER_OF_ACTIVE( Lock ) - 1 );
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now actually do the release.
|
|||
|
//
|
|||
|
|
|||
|
ExReleaseResource( &Lock->Resource );
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // SrvReleaseLock
|
|||
|
|
|||
|
#endif // SRVDBG_LIST
|
|||
|
|
|||
|
|
|||
|
#if SRVDBG_LIST || SRVDBG_LOCK
|
|||
|
|
|||
|
ULONG
|
|||
|
SrvCheckListIntegrity (
|
|||
|
IN PLIST_ENTRY ListHead,
|
|||
|
IN ULONG MaxEntries
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This debug routine checks the integrity of a doubly-linked list by
|
|||
|
walking the list forward and backward. If the number of elements is
|
|||
|
different in either direction, or there are too many entries in the
|
|||
|
list, execution is stopped.
|
|||
|
|
|||
|
*** It is the responsibility of the calling routine to do any
|
|||
|
necessary synchronization.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ListHead - a pointer to the head of the list.
|
|||
|
|
|||
|
MaxEntries - if the number of entries in the list exceeds this
|
|||
|
number, breakpoint.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ULONG - the number of entries in the list.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PLIST_ENTRY current;
|
|||
|
ULONG entriesSoFar;
|
|||
|
ULONG flinkEntries;
|
|||
|
|
|||
|
for ( current = ListHead->Flink, entriesSoFar = 0;
|
|||
|
current != ListHead;
|
|||
|
current = current->Flink ) {
|
|||
|
|
|||
|
if ( ++entriesSoFar >= MaxEntries ) {
|
|||
|
SrvPrint2( "Seen %ld entries in list at %lx\n",
|
|||
|
entriesSoFar, ListHead );
|
|||
|
DbgBreakPoint( );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
flinkEntries = entriesSoFar;
|
|||
|
|
|||
|
for ( current = ListHead->Blink, entriesSoFar = 0;
|
|||
|
current != ListHead;
|
|||
|
current = current->Blink ) {
|
|||
|
|
|||
|
if ( ++entriesSoFar >= MaxEntries ) {
|
|||
|
SrvPrint2( "Seen %ld entries in list at %lx\n",
|
|||
|
entriesSoFar, ListHead );
|
|||
|
DbgBreakPoint( );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( flinkEntries != entriesSoFar ) {
|
|||
|
SrvPrint3( "In list %lx, Flink entries: %ld, Blink entries: %lx\n",
|
|||
|
ListHead, flinkEntries, entriesSoFar );
|
|||
|
DbgBreakPoint( );
|
|||
|
}
|
|||
|
|
|||
|
return entriesSoFar;
|
|||
|
|
|||
|
} // SrvCheckListIntegrity
|
|||
|
|
|||
|
#endif // SRVDBG_LIST || SRVDBG_LOCK
|
|||
|
|
|||
|
|
|||
|
#if SRVDBG_LIST
|
|||
|
|
|||
|
VOID
|
|||
|
SrvIsEntryInList (
|
|||
|
IN PLIST_ENTRY ListHead,
|
|||
|
IN PLIST_ENTRY ListEntry
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This debug routine determines whether the specified list entry is
|
|||
|
contained within the list. If not, execution is stopped. This is
|
|||
|
meant to be called before removing an entry from a list.
|
|||
|
|
|||
|
*** It is the responsibility of the calling routine to do any
|
|||
|
necessary synchronization.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ListHead - a pointer to the head of the list.
|
|||
|
|
|||
|
ListEntry - a pointer to the entry to check.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PLIST_ENTRY checkEntry;
|
|||
|
|
|||
|
//
|
|||
|
// Walk the list. If we find the entry we're looking for, quit.
|
|||
|
//
|
|||
|
|
|||
|
for ( checkEntry = ListHead->Flink;
|
|||
|
checkEntry != ListHead;
|
|||
|
checkEntry = checkEntry->Flink ) {
|
|||
|
|
|||
|
if ( checkEntry == ListEntry ) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if ( checkEntry == ListEntry ) {
|
|||
|
SrvPrint2( "Entry at %lx supposedly in list at %lx but list is "
|
|||
|
"circular.", ListEntry, ListHead );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we got here without returning, then the entry is not in the
|
|||
|
// list and something has gone wrong.
|
|||
|
//
|
|||
|
|
|||
|
SrvPrint2( "SrvIsEntryInList: entry at %lx not found in list at %lx\n",
|
|||
|
ListEntry, ListHead );
|
|||
|
DbgBreakPoint( );
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // SrvIsEntryInList
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SrvIsEntryNotInList (
|
|||
|
IN PLIST_ENTRY ListHead,
|
|||
|
IN PLIST_ENTRY ListEntry
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This debug routine determines whether the specified list entry is
|
|||
|
contained within the list. If it is, execution is stopped. This is
|
|||
|
meant to be called before inserting an entry in a list.
|
|||
|
|
|||
|
*** It is the responsibility of the calling routine to do any
|
|||
|
necessary synchronization.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ListHead - a pointer to the head of the list.
|
|||
|
|
|||
|
ListEntry - a pointer to the entry to check.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PLIST_ENTRY checkEntry;
|
|||
|
|
|||
|
//
|
|||
|
// Walk the list. If we find the entry we're looking for, break.
|
|||
|
//
|
|||
|
|
|||
|
for ( checkEntry = ListHead->Flink;
|
|||
|
checkEntry != ListHead;
|
|||
|
checkEntry = checkEntry->Flink ) {
|
|||
|
|
|||
|
if ( checkEntry == ListEntry ) {
|
|||
|
|
|||
|
SrvPrint2( "SrvIsEntryNotInList: entry at %lx found in list "
|
|||
|
"at %lx\n", ListEntry, ListHead );
|
|||
|
DbgBreakPoint( );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we got here without returning, then the entry is not in the
|
|||
|
// list, so we can return.
|
|||
|
//
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // SrvIsEntryNotInList
|
|||
|
|
|||
|
#endif // SRVDBG_LIST
|