This documentation is archived and is not being maintained.

LockManager Class

LockManager class.


Namespace:  Microsoft.TeamFoundation.Framework.Server
Assembly:  Microsoft.TeamFoundation.Framework.Server (in Microsoft.TeamFoundation.Framework.Server.dll)

public class LockManager

The LockManager type exposes the following members.

Public methodLockManagerConstructor

Public methodAssertLockHeld(Object, LockManager.LockType, Int64)Assert that the given lock is held by the current thread (debug assert).
Public methodAssertLockHeld(ILockName, LockManager.LockType, Int64)Assert that the given lock is held by the current thread (debug assert).
Public methodAssertLockNotHeld(Object, LockManager.LockType, Int64)Assert that the given lock is not held by the current thread (debug assert).
Public methodAssertLockNotHeld(ILockName, LockManager.LockType, Int64)Assert that the given lock is not held by the current thread (debug assert).
Public methodAssertNoLocksHeld(Int64)Assert that the current thread holds no LockManager locks.
Public methodAssertNoLocksHeld(LockManager.LockType, Int64)Assert that the given lock is not held by the current thread (debug assert).
Public methodAssertZeroActiveLockObjectsAsserts iff there are active locks in the lock manager (where refcount > 0)
Public methodStatic memberCompareLockTypesCompares two lock types (throws if lock types are not comparable).
Public methodEqualsDetermines whether the specified object is equal to the current object. (Inherited from Object.)
Protected methodFinalizeAllows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection. (Inherited from Object.)
Public methodGetHashCodeServes as the default hash function. (Inherited from Object.)
Public methodGetLock(Object, LockManager.LockType, Int64)Get a lock.
Public methodGetLock(ILockName, LockManager.LockType, Int64)Get a named lock.
Public methodGetTypeGets the Type of the current instance. (Inherited from Object.)
Public methodHasLocksReturns true if this requestId owns any locks
Public methodLock(Object, Int64)Get a leaf monitor lock for a given object.
Public methodLock(Object, LockManager.LockType, Int64)Get an object monitor lock.
Public methodLock(ILockName, LockManager.LockType, Int64)Get a named lock.
Protected methodMemberwiseCloneCreates a shallow copy of the current Object. (Inherited from Object.)
Public methodReleaseAnyLockRelease the most nested lock of a given lock type and any name.
Public methodReleaseLock(Object, LockManager.LockType, Int64)Release a lock.
Public methodReleaseLock(ILockName, LockManager.LockType, Int64)Release a named lock.
Public methodTestLock(Object, LockManager.LockType, Int64)Test if this thread already holds a lock.
Public methodTestLock(String, LockManager.LockType, Int64)Test if this thread already holds a lock.
Public methodToStringReturns a string that represents the current object. (Inherited from Object.)
Public methodTryGetLock(Object, LockManager.LockType, Int64)Try to get a lock.
Public methodTryGetLock(ILockName, LockManager.LockType, Int64, Int32)Get a named lock

Managed Store practices deadlock avoidance. All locking must use the LockManager locks. Every lock is assigned a level (a position in the locking hierarchy), and any operation can only request locks that are higher in the hierarchy than any lock currently held.

LockManager supports the concept of "named locks", e.g. when we must lock some entity for which we may not have a stable object in memory, but have just a name of such entity. Examples are mailbox and database. Any object associated with mailbox or database can come and go, all we have stable for such entity is its name, such as a database GUID for a database or mailbox number for a mailbox. We support monitor locks and reader-writer locks for named locks.

LockManager also supports ordinary "object locks", when we must lock a particular object instance in memory. Only Monitor locks are currently supported for object locks, that is the same locking mechanism as used in C# "lock" statement. In contrast to the "lock" statement, LockManager object lock fully participates in a locking hierarchy, therefore we can verify they are used in a correct order.

One special case of "object lock" is a "leaf object lock". We do not have to specify the lock level for such lock - it is expected to always be a most-nested lock and no other locks can be taken when holding such leaf lock.

"Named locks" are implemented by dynamically allocating a lock object for each unique name, and storing them in a global dictionary. Thus accessing a named lock by name requires dictionary lookup to find a corresponding lock object. The lock object dictionary should itself be locked while you perform such lookup. All this makes named locks potentially more expensive than ordinary locks, because of the additional cost of locking the dictionary and a dictionary lookup. We use two techniques to reduce such cost: (1) partitioning lock object dictionary, to reduce the dictionary global lock contention, and (2) providing caller the ability to cache the named lock object reference and bypass the dictionary lookup most of the time. Note that partitioning alone is insufficient because it does not help much with relatively wide-scope locks, such as a database lock; for example, when everybody wants to grab the same shared lock.

There is potentially unbounded number of unique lock names. Therefore, the number of named lock objects we can potentially create is also unbounded. Because references to named lock objects are stored in a global dictionary, such objects could never be automatically garbage-collected. Hence we want to be able to clean up named lock objects that are not regularly used. To support thread-safe cleanup of named lock objects, such objects are refcounted. Every lock taken on a named lock object requires having such object "addrefed", the reference should be released after lock is released. Cleanup logic checks that the object is not currently referenced before removing it from the dictionary. After a named lock object is removed from a dictionary, it is marked as disposed and cannot be addrefed any more. An attempt to lock the same name next time will cause allocating a new named lock object that has the same name and adding it back to a dictionary. Thus it is OK to have stale named lock object references cached by a caller - such stale reference will be detected and updated the next time we try to lock it and allocate a new lock object.

We use a simple time based heuristic to clean up unused lock objects. On every N named lock release calls we check if there is a time to run cleanup, and then examine the dictionary and collect all unreferenced objects which are not used recently. Then we attempt to dispose every such object and remove its reference from a dictionary. Cleanup is per dictionary partition, so that we do not have to lock other partitions when we run cleanup for any given partition.

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.