6

Can someone explain, in detail, why it's possible to lock on objects of any type in C#?

I understand what lock is for and how to use it. I know how it expands to Monitor.Enter/Exit. What I'm looking for an explanation of the implementation detail and design considerations.

First of all: What is happening under the hood? For example: are there extra bits in an object instance (like there is for RTTI/vtable) that make it work? Or some kind of lookup table keyed on object references? (If so, how does this interact with the GC?) Or something else? Why don't I have to create an instance of a specific type to hold whatever the lock data is?

(And, by the way, what do Enter and Exit map to in native code?)

And, secondly, why is .NET designed to not have a specific type for taking out locks on? (Given that you generally just make a new object() for the purpose anyway - and most cases where you lock "any old object" are problematic.) Was this design choice forced by the implementation detail? Or was it deliberate? And, if deliberate, was it a good choice? (I realise this second part may require speculation.)

Andrew Russell
  • 26,924
  • 7
  • 58
  • 104
  • 1
    For the second question, I think it's likely that the .NET designers wanted greater compatibility with Java (just like the broken array covariance feature). – Michael Liu Nov 26 '12 at 00:53
  • Ah, of course - Java. Although I'd still be interested in hearing how the design considerations might play out if things like Java compatibility aren't a factor. – Andrew Russell Nov 26 '12 at 00:56

1 Answers1

4

It is possible to lock on all non struct types. In layout of each refernce type on the heap there is special field (sync block) that is used to manage lock. The layout is covered in details in How CLR Creates Runtime Object. Excerpt from article is below:

The OBJECTREF does not point to the beginning of the Object Instance but at a DWORD offset (4 bytes). The DWORD is called Object Header and holds an index (a 1-based syncblk number) into a SyncTableEntry table.

Object layout on the heap:

sync block index 
pointer to type
fields...

Speculation part: I believe that original guidance was to lock on whatever is convinient, but it was relatively quickly changed to having special "private object for lock" due to ease of getting external code to deadlock your methods. I think there even were classes in Framework that were impelented with locking on publically visible objects...

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • So - my understanding is that, basically, the vast majority of reference objects (that never get locked) are "wasting" 4 bytes to store a sync block index. Correct? This seems like a pretty strong driver towards designing a specific type for locking on. The only compelling reason to design in the other direction seems to be Java compatibility (as Michael Lui suggested in comments). Given hindsight is seems like a poor design - unless there's another factor I'm missing? – Andrew Russell Nov 26 '12 at 02:40
  • @AndrewRussell, yes - all have that index, most never use it. I don't know actual reasons of why this decision was made... so hopefully someone close to runtime can tell the story. – Alexei Levenkov Nov 26 '12 at 02:45
  • I just found an interesting tidbit that the sync block index was (also) used to store a unique ID for `GetHashCode` in version 1.x of the framework. Another indicator as to why .NET works that way. But still no compelling design basis. – Andrew Russell Nov 26 '12 at 02:52