Monitors
Process ~ thread
Monitor {
private data
A() { <- acquire inserted by compiler
...
wait
...
} <- release inserted by compiler
B() {
...
signal
..
}
C() {
...
}
}
- Only one thread can execute in the monitor at once
- How? Monitor provides mutual exclusion, compiler inserts calls to acquire and release (lock)
2 main uses for synchronization
- Mutual exclusion (1 at a time)
- coordination (order of when to run)
Producer and Consumer - example of coordination
Imagine if we had a pool of resources that we want to share among threads
Producer Consumer
ā Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ā You can decompress Drawing data with the command palette: āDecompress current Excalidraw fileā. For more info check in plugin settings under āSavingā
Excalidraw Data
Text Elements
Buffer Pool
Malloc
Free
Producers
Consumers
Design:
- need to make sure not to corrupt buffer pool (mutual ex)
- consumer need someway to block while holding on lock without blocking a producer
Condition variables
- Goal: allow threads inside a monitor to wait
- wait - suspend the thread, releases the lock
- signal - wakes up a waiting thread, thread reacquires the lock (lock to monitor)
Hoare vs Mesa Semantics
Hoare Semantics
- wait - puts the thread on a queue
- signal - wake up a waiter, context switch to it
Hoare Semantics for Monitors
ā Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ā You can decompress Drawing data with the command palette: āDecompress current Excalidraw fileā. For more info check in plugin settings under āSavingā
Excalidraw Data
Text Elements
Signaller
Waiter
Time
Signal
return from wait()
Leave the monitor
Return from signal
Notes:
- very consistent, Horace want to prove the correctness
- if it goes in an orchestrated way itās easier to prove
Invariant I - consistency of data structures managed by the monitor
- data structures might not be in a consistent state
- when you exit the monitor, it has to all be in a consistent state (need to restore the invariant)
- when can a program leave the monitor?
- exit
- wait
- signal
- Assume I is true:
- when you can enter the monitor
- return from wait or signal Condition B - the situation that the waiter is waiting for change
- different from condition variable (primitive for synchronization), condition B - abstract idea of whatās blocking the waiter
- when can we assume that the condition is true?
- thread returns from wait
- when a thread calls signal
Invariant:
| Before | op | After |
|---|---|---|
| Invariant True | wait | Invariant True |
| Invariant True | signal | Invariant True |
| Before | op | After |
| wait | B True | |
| B True | signal | |
| Note: |
- before signal waiter is waiting to be signaled
- after calling wait, waiter should be waiting
- B = isWaiting
Mesa Semantics
- Weakens the semantics, from Hoarce
- wait - puts the thread on a queue (same)
- signal (notify) - puts the thread on the runnable queue, continues running
Mesa Lamport
ā Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ā You can decompress Drawing data with the command palette: āDecompress current Excalidraw fileā. For more info check in plugin settings under āSavingā
Excalidraw Data
Text Elements
signaller
waiter
signal
exit monitor
runnable
return from wait
Note:
Link to original
- signal not using the monitor
Mesa Semantics Differences from Horace
- When we return from wait the condition might not be true anymore
Horace Semantics:
if (!condition)
wait
Mesa semantics:
while (!condition)
wait
What we know to be true:
| Before | op | After |
|---|---|---|
| Invariant True | wait | Invariant True |
| signal | ||
| Before condition | op | After condition |
| wait | ||
| signal |
- The signal will check is it possible to wake up a thread or not, so the condition doesnāt necessarily need to be true
- Condition doesnāt necessarily have to be true with wait because other threads might have ran while waiting.
- When you leave the monitor you need to restore the state, thatās why wait has Invariant True before and after.
Semaphores vs Monitors
Hoare established that they are equivalent. Hoare establish that you can implement both with each other
Mesa
Types of methods:
- entry - entering monitor
- internal - modifies state
- external - donāt modify state
| Hoare | Mesa | Java | |
|---|---|---|---|
| Monitor lock | All methods | entry methods | synchronized keyword (it will behave like a monitor method) |
| condition variables | explicit | explicit | implicit CVs (associated with the class itself) explicit CVs |
| wait | Hoare Semantics (if cond) | Mesa Semantics (while cond) | Mesa Semantics (while cond) |
| signal | Hoare Semantics | Mesa Semantics (notify, broadcast) | Mesa Semantics (notify, notifyAll) |
| graunlarity | coarse (like entire file systems, big chunks of os) | monitor record (more fine grained) | multiple different granularities (static methods that are synchronized, non static methods with synchronized keyword, or synchronized keyword for block of code) |
| abort | ? didnāt consider | unwind handler (restore data structures to a consistent state) | exception handlers |
| nesting | ? didnāt consider | let first call hold lock | similar (let programmer decide) |
| Broadcast will not be possible with Hoare Semantics because the process will need to context switch between locks/monitors | |||
| Nesting: Mesa Semantics let programmer decide | |||
|
Trends over time
Hoare and Mesa
- rely on compiler support Java
- compiler support - implicit CVs, using synchronize
- programmer-managed locks with explicit CVs C++, Rust, Python
- no monitors
- but do have CVs and locks
- programmer must acquire locks
- but compiler can handle release
- makes it easy for programmer if you had a function that exits in many different ways and you want to make sure that a release is conducted
- but compiler can handle release
Summary
- language support for synchronization with monitors
- Hoare vs Mesa Semantics
- Practical Challenges
- aborts, nesting etc.