Reputation: 852
So I read that monitors enforce mutual exclusion, mostly by using 2 procedures wait() and notify()/signal() and I also understand problems solved using the monitor.
What I want to know is how does a monitor enforce mutual exclusion? What happens inside wait() and notify()?
Why can only 1 thread invoke a monitor procedure? How is this constraint enforced?
(I am sorry if I am not very clear)
Upvotes: 0
Views: 1374
Reputation: 10903
A monitor is a combination of two threading primitives: the mutex and the condition variable.
A mutex is an object that can be "locked" by only one thread at a time. If a second thread attempts to "lock" the mutex, the second thread is forced to wait until the first thread "unlocks" the mutex.
There are many fast implementations of mutexes which use atomic operations to lock and unlock without kernel help, but all of them eventually fall back on asking the OS to lock the mutex for them. The OS will try to lock the mutex for you, but if it is already locked, the OS will simply suspend the thread and mark that it should be reawakened when that particular mutex is unlocked
A condition variable is a particularly interesting beast which solves a few particular problems with mutexes. In particular, waiting on a mutex is not interruptable. Once you start, you are comited to waiting for the mutex to come available. You can't interrupt a mutex wait with, say, an exception. Mutexes simply gave up that capability in return for their raw speed (they are MUCH faster than other threading primitives).
Also, mutexes are not always guaranteed to be fair, and some algorithms really have trouble with unfair wait times. It is much easier to make a condition variable be fair, because it doesn't have to have the same runtime requirements.
Of course, trying to solve these issues while playing nicely with mutexes is tricky. Condition variables have the following functions
Wait is the oddest of functions. Why it has to be defined in that way is well beyond the scope of the question.
With these primitives, one can pseudocode the monitor
class Monitor
{
private Mutex mutex;
private ConditionVariable cond;
public Monitor()
{
mutex = new Mutex();
cond = new ConditionVariable();
}
public void enter()
{
mutex.lock();
}
public void exit()
{
mutex.unlock();
}
public void wait()
{
// mutex should be locked already
cond.wait(mutex); // wait unlocks the mutex while waiting, relocks after waiting
}
public void notify()
{
cond.notify();
}
public void broadcast()
{
cond.broadcast();
}
};
As to the particular functions you were interested in:
wait() is called when a thread holds the lock. At the OS level, it adds the current thread to a queue of threads to wake up on the condition variable, releases the lock, and tells the scheduler to put the thread to sleep (all three happen "atomically," meaning the OS won't stop half way and switch to another thread). When it is woken up (with a notify or broadcast), it will reacquire the lock before returning to your code.
notify() looks at the first thread in the queue, removes it from the queue, and tells the OS scheduler to begin scheduling that thread again.
mutex locking is usually done using a special operation called "compare/exchange" (or one of the variants on it). Compare exchange does the following algorithm on an "atomic value":
Those are all the pieces, in a firehose fashion. If you want to know more, I highly recommend wikipediaing any of the unfamiliar nouns. The threading pages are actually quite good there. (Mutex, Condition Variable, Sleeping Barber, and Compare Exchange are all good searches)
Upvotes: 2
Reputation: 36304
Assume a monitor to be a lock and your house as a resource. Now, Whenever any thread wants to enter your house, it has to first acquire the lock to your house. As long as that thread holds the lock, no other thread can enter the house (access that resource).
wait()
--> The thread which is currently holding the lock, releases the lock on that object/ resource and waits for other threads to notify()
it so that it can continue its execution.
notify()
--> The current thread notifies that it is releasing the lock on that resource. All threads which are waiting for this lock, get notified. The OS is smart enough to pick one of these threads, provide the lock to it, and execute it..
Upvotes: 0