Why don't critical sections work cross process?
I could have sworn this was answered in a previous blog by someone else (Raymond, Eric Lippert, etc), but...
Someone sent me feedback asking:
Q> Why can't critical section objects be used across processes compared to mutexes?
Originally, I thought "Man, that's a silly question, it's obvious".
But then I realized that it's not obvious, because critical sections are different from every other external synchronization mechanism in Windows (there are some internal synchronization mechanisms that share characteristics with critical sections, but they're not public).
You see, a critical section isn't a native object type in Windows. All the other synchronization primitives (mutexes, events, semaphores, etc) are native objects - the user mode semaphore (or mutex, or event) is an object maintained by NT's object manager. As such, it has an ACL, a name, all the things that go with being a native object.
And since these synchronization primitives are maintained by the object manager, they can be shared across processes - another process can open a named handle, or you can dup the handle into another process, or you can have the process be inherited by a child process.
But critical sections are special.
You see, the flexibility that you get by being maintained by the NT object manager has a cost associated with it - every operation that's performed on the semaphore/mutex/event requires a user mode to kernel mode transition, as does waiting on the object.
Sometimes that cost is too high - there's a need for a highly performant lock structure that can be used to protect a region of code. That's where the critical section comes into play.
A critical section is just a structure, it contains a whole bunch of opaque fields. Inside the EnterCriticalSection routine is code that uses interlocked instructions to acquire the structure without entering the kernel - it's what makes the critical section so fast.
Of course, the fact that the critical section is just a structure is also why it can't be shared between processes - since it's just a chunk of memory that's in the processes address space, it's not accessible to other processes.
The clever observer now realizes that this that begs the question: What happens if I initialize a critical section in a shared memory region - after all, it's just a chunk of memory, I can share a memory region between two processes, and just initialize a critical section in the shared memory region.
This might actually work, for a while. But the thing about critical sections is that they're more than just a spin lock. There's also a semaphore that's acquired when the critical section has contention. And that semaphore isn't shared between processes (actually, the semaphore isn't even "allocated" until there's contention (it's not allocated, per se)). If that wasn't enough, there are also fields within the critical section that point to other external data structures as well - those structures won't exist in the process that didn't initialize the critical section. There's no way of knowing what will happen if the other process enters the critical section. If you're lucky, the process will crash. If you're not, you might "just" corrupt memory.
This is a really long answer to a really short question, but sometimes its worth digging into it a bit.
Comments
- Anonymous
August 24, 2005
The comment has been removed - Anonymous
August 24, 2005
Sorry, I missed the <sarcasm> tag there :)
You're totally right - it could be horrible. - Anonymous
August 24, 2005
The short answer to the question is "Because if it did, it'd be called a 'mutex'." - Anonymous
August 24, 2005
> I could have sworn this was answered in a previous blog by someone else...
IIRC, Matt Pietrek wrote a couple of columns about them in his former MSJ column. - Anonymous
August 24, 2005
Since critical sections are non-kernel objects, it is possible to implemenet them yourself - using shared memory and a shared (named) event. In "Programming Applications for MS Windows", Jeffery Richter came up with a cross-process critical section-like synchronization mechanism, which he calls Optex. A Windows critical section lazily allocates an event kernel object if it has to deal with contention, but the Optex eagerly allocates one on initialization. - Anonymous
August 24, 2005
> I could have sworn this was answered in a previous blog by someone else (Raymond, Eric Lippert, etc), but...
Wasn't me. I'm far from an expert on these sorts of concurrency issues. Interesting post! - Anonymous
August 24, 2005
anon - the problem with that solution (a kernel object per critsec) doesn't scale - to be truly lightweight, you need to be able to have thousands and thousands of critical sections in a process.
While you can have thousands and thousands of kernel objects, there's a cost associated with them, and it can become prohibitive. - Anonymous
August 25, 2005
Anon, why wouldn't you use mutex instead of using an event and an additional synchronization code? Once you create a kernel synchronization object (a named event in the case you describe) you just lost the advantage a critical section gives you (not going to the kernel) so you may just use a kernel synchronization object to do your synchronization. - Anonymous
August 25, 2005
I didn't propose to replace Windows critical sections by Optex objects; I just pointed out their existence.
I doubt someone will need thousands and thousands of these shared between processes, and the alternatives have similar costs anyway. - Anonymous
August 25, 2005
The comment has been removed - Anonymous
August 26, 2005
Cross-process synchronization with locks protecting some shared state is generally non-robust. People who use it risk a cascade of failures once a single process in a synchronization chain fails. (One process dies while holding the lock, the others must somehow come to terms with the inconsistent state that results).
Unlike a critical section, mutexes report when they're being "abandoned". Perhaps a very clever use of this feature could be made to somehow keep the other process continue running. If somebody actually did this, I'd be interested to hear. - Anonymous
September 01, 2005
Incidentally, while your usage of "begs the question" may be gaining in popularity, its original meaning is quite different than most people assume:
http://www.worldwidewords.org/qa/qa-beg1.htm