Monitor (synchronization)
From Wikipedia, the free encyclopedia
A monitor is an approach to synchronize two or more computer tasks that use a shared resource, usually a hardware device or a set of variables. With monitor-based concurrency, the compiler or interpreter transparently inserts locking and unlocking code to appropriately designated procedures, instead of the programmer having to access concurrency primitives explicitly.
Invented by Per Brinch Hansen, first implemented in the Concurrent Pascal language and used to structure inter-process communication in the Solo Operating System.
Contents |
[edit] Mutual exclusion
A monitor consists of:
- a set of procedures that allow interaction with the shared resource
- a mutual exclusion lock
- the variables associated with the resource
- a monitor invariant that defines the assumptions needed to avoid race conditions
A monitor procedure takes the lock before doing anything else, and holds it until it either finishes or waits for a condition (explained below). If every procedure guarantees that the invariant is true before it releases the lock, then no task can ever find the resource in a state that might lead to a race condition.
As a simple example, consider a monitor for performing transactions on a bank account.
monitor account { int balance := 0 function withdraw(int amount) { if amount < 0 then error "Amount may not be negative" else if balance < amount then error "Insufficient funds" else balance := balance - amount } function deposit(int amount) { if amount < 0 then error "Amount may not be negative" else balance := balance + amount } }
The monitor invariant in this case simply says that the balance must reflect all past operations before another operation can begin. It is usually not stated in the code but may be mentioned in comments. There are however programming languages like Eiffel, which can check invariants. The lock is added by the compiler. This makes monitors safer and more reliable than approaches that require the programmer to insert locking and unlocking operations by hand, since the programmer can forget them.
[edit] Condition variables
To avoid entering a busy waiting state, processes must be able to signal each other about events of interest. Monitors provide this capability through condition variables. When a monitor function requires a particular condition to be true before it can proceed, it waits on an associated condition variable. By waiting, it gives up the lock and is removed from the set of runnable processes. Any process that subsequently causes the condition to be true may then use the condition variable to notify a process waiting for the condition. A process that has been notified regains the lock and can proceed.
The following monitor uses condition variables to implement an interprocess communication channel that can store only one integer value at a time.
monitor channel { int contents boolean full := false condition snd condition rcv function send(int message) { while full do wait(rcv) //Mesa Semantics: See Explanation Below contents := message full := true notify(snd) } function receive() { var int received while not full do wait(snd) //Mesa Semantics: See Explanation Below received := contents full := false notify(rcv) return received } }
Note that since waiting on a condition forfeits the lock, the waiter must make sure the monitor invariant is satisfied before it waits. In the example above, the same is true for notifying.
[edit] Hoare vs. Mesa semantics
In early style monitor implementations (known as Hoare semantics), notifying a condition variable caused a waiting process to receive the lock and run immediately, thereby guaranteeing that the condition would still be true. Implementing this behavior is complicated and has a high overhead. It is also incompatible with schedulers that can interrupt a process arbitrarily. For these reasons, researchers have considered various other semantics for condition variables.
In most modern implementations (known as Mesa semantics), notifying does not take control away from the running process, but merely makes some waiting process runnable. The notifying process continues to hold the lock until it leaves the monitor function. The side effects of this approach are that the notifying process does not have to set up the monitor invariant before notifying, and the waiting process must double-check the condition it was waiting for. Specifically, if a monitor function includes the expression if test then wait(cv)
, another process could enter the monitor after the notification and invert the sense of test
before the waiting process runs. The expression must be rewritten as while test do wait(cv)
so that the condition is re-checked before the process continues.
Implementations also provide a "notifyAll" or "broadcast" operation that notifies every process waiting on a given condition. This operation is useful, for example, when several processes are waiting for different amounts of storage to become available. Releasing storage can enable any number of these processes to proceed, but the scheduler does not know which ones.
A sample implementation for a condition variable is as follows:
conditionVariable { int queueSize = 0; semaphore lock; semaphore waiting; wait() { lock.acquire(); queueSize++; lock.release(); waiting.down(); } signal() { lock.acquire(); while (queueSize > 0){ queueSize--; waiting.up(); } lock.release(); } }
[edit] History
Per Brinch Hansen was the first to describe and implement monitors, basing them on ideas from C. A. R. Hoare. Hoare subsequently developed the theoretical framework and demonstrated their equivalence to semaphores (when using the original semantics).
Programming languages that have supported monitors include
- Ada
- C# (and other languages that use the .NET Framework)
- Concurrent Pascal
- D programming language
- Java (via the synchronized keyword)
- Mesa
- Modula-3
- Ruby (programming language)
- Squeak Smalltalk
- uC++
[edit] See also
- Mutual exclusion
- Communicating sequential processes - a later development of monitors by C. A. R. Hoare
[edit] Bibliography
- Monitors: an operating system structuring concept, C. A. R. Hoare - Communications of the ACM, v.17 n.10, p.549-557, Oct. 1974 [1]
- Monitor classification P.A. Buhr, M. Fortier, M.H. Coffin - ACM Computing Surveys (CSUR), 1995 [2]
[edit] External links
- "Monitors: An Operating System Structuring Concept" by Charles Antony Richard Hoare
- "Signalling in Monitors" by John H. Howard
- "Experience with Processes and Monitors in Mesa" by Butler W. Lampson and David D. Redell
- pthread_cond_wait - description from the Open Group Base Specifications Issue 6, IEEE Std 1003.1
- "Block on a Condition Variable" by Dave Marshall
- "Strategies for Implementing POSIX Condition Variables on Win32" by Douglas C. Schmidt and Irfan Pyarali
- Condition Variable Routines from the Apache Portable Runtime Library
- wxCondition description
- boost::condition class description
- ZThread Condition Class Reference
- Wefts::Condition Class Reference
- ACE_Condition Class Template Reference
- QWaitCondition Class Reference
- Common C++ Conditional Class Reference
- at::ConditionalMutex Class Reference
- threads::shared - Perl extension for sharing data structures between threads
- Tutorial multiprocessing traps
- http://msdn.microsoft.com/en-us/library/ms682052(VS.85).aspx