Key Points
Monitors expose the ability to take and release the sync block lock on an object on demand via Enter, TryEnter and Exit.
The Monitor Wait, Pulse and PulseAll methods are related to SyncBlocks. It is necessary to be in a synchronized region on an object before calling Wait or Pulse on that object. Wait releases the lock if it is held and waits to be notified. When Wait is notified, it returns and has obtained the lock again. Notify signals for next thread in wait queue to proceed.
Each object has an associated SyncBlock index. If an object has not been used in a synchronization operation then the associated SyncBlock index is empty. When the object is used in a synchronization operation then a SyncBlock is retrieved from the SyncBlock cache. The SyncBlock is associated with the object by updating the object’s SyncBlock index.
A SyncBlock is a sparsely allocated data structure. It currently serves several purposes:
Generally most objects will not have SyncBlocks, unless a synchronization operation has performed against the object. Furthermore, the SyncBlock is accessed using a SyncBlockIndex which is allocated at a negative offset on the managed object. All objects have a SyncBlockIndex, but most of these indices have a value of 0 - meaning they share a single dummy SyncBlock. Logically, such objects don’t have a SyncBlock.
However, there’s a set of objects that have non-zero SyncBlockIndexes, but they don’t actually have SyncBlocks. These are objects that don’t otherwise need a SyncBlock, but which have had their Object.GetHashCode method called. If a class uses the default (runtime provided) implementation of GetHashCode, this method will allocate a SyncBlockIndex but will not allocate the SyncBlock (to save memory). The result is a relatively unique and stable hash code.
It is only relatively unique because SyncBlockIndexes will be recycled when objects are collected by GC, and because other classes which override GetHashCode can certainly return the same numbers. Also, it’s relatively stable because it does not persist with the object. It is only valid so long as the object is activated within a particular EE instance.
For reference details see: Object.