[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]
Assembly: Microsoft.TeamFoundation.Framework.Server (in Microsoft.TeamFoundation.Framework.Server.dll)
Thetype exposes the following members.
|AssertLockHeld(Object, LockManager.LockType, Int64)||Assert that the given lock is held by the current thread (debug assert).|
|AssertLockHeld(ILockName, LockManager.LockType, Int64)||Assert that the given lock is held by the current thread (debug assert).|
|AssertLockNotHeld(Object, LockManager.LockType, Int64)||Assert that the given lock is not held by the current thread (debug assert).|
|AssertLockNotHeld(ILockName, LockManager.LockType, Int64)||Assert that the given lock is not held by the current thread (debug assert).|
|AssertNoLocksHeld(Int64)||Assert that the current thread holds no LockManager locks.|
|AssertNoLocksHeld(LockManager.LockType, Int64)||Assert that the given lock is not held by the current thread (debug assert).|
|AssertZeroActiveLockObjects||Asserts iff there are active locks in the lock manager (where refcount &gt; 0)|
|CompareLockTypes||Compares two lock types (throws if lock types are not comparable).|
|Equals||Determines whether the specified object is equal to the current object. (Inherited from Object.)|
|Finalize||Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection. (Inherited from Object.)|
|GetHashCode||Serves as the default hash function. (Inherited from Object.)|
|GetLock(Object, LockManager.LockType, Int64)||Get a lock.|
|GetLock(ILockName, LockManager.LockType, Int64)||Get a named lock.|
|GetType||Gets the Type of the current instance. (Inherited from Object.)|
|HasLocks||Returns true if this requestId owns any locks|
|Lock(Object, Int64)||Get a leaf monitor lock for a given object.|
|Lock(Object, LockManager.LockType, Int64)||Get an object monitor lock.|
|Lock(ILockName, LockManager.LockType, Int64)||Get a named lock.|
|MemberwiseClone||Creates a shallow copy of the current Object. (Inherited from Object.)|
|ReleaseAnyLock||Release the most nested lock of a given lock type and any name.|
|ReleaseLock(Object, LockManager.LockType, Int64)||Release a lock.|
|ReleaseLock(ILockName, LockManager.LockType, Int64)||Release a named lock.|
|TestLock(Object, LockManager.LockType, Int64)||Test if this thread already holds a lock.|
|TestLock(String, LockManager.LockType, Int64)||Test if this thread already holds a lock.|
|ToString||Returns a string that represents the current object. (Inherited from Object.)|
|TryGetLock(Object, LockManager.LockType, Int64)||Try to get a lock.|
|TryGetLock(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.