CSE 120 Principles of Operating Systems Fall 2014

CSE 120
Principles of Operating
Systems
Fall 2014
Lecture 6: Semaphores and Monitors
Geoffrey M. Voelker
Administrivia



2nd Discussion: Mon 4-5pm in CSE 2154
Homework #2
Project #1
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
2
Higher-Level Synchronization


We looked at using locks to provide mutual exclusion
Locks work, but they have limited semantics


Instead, we want synchronization mechanisms that



Block waiters, leave interrupts enabled in critical sections
Provide semantics beyond mutual exclusion
Look at two common high-level mechanisms



Just provide mutual exclusion
Semaphores: binary (mutex) and counting
Monitors: mutexes and condition variables
Use them to solve common synchronization problems
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
3
Semaphores

Semaphores are an abstract data type that provide
mutual exclusion to critical sections


Semaphores can also be used as atomic counters


Described by Dijkstra in THE system in 1968
More later
Semaphores are “integers” that support two operations:

Semaphore::Wait(): decrement, block until semaphore is open
» Also P(), after the Dutch word for test, or down()

Semaphore::Signal: increment, allow another thread to enter
» Also V() after the Dutch word for increment, or up()


That's it! No other operations – not even just reading its value
Semaphore safety property: the semaphore value is
always greater than or equal to 0
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
4
Blocking in Semaphores


Associated with each semaphore is a queue of waiting
processes
When wait() is called by a thread:



If semaphore is open, thread continues
If semaphore is closed, thread blocks on queue
Then signal() opens the semaphore:


If a thread is waiting on the queue, the thread is unblocked
If no threads are waiting on the queue, the signal is
remembered for the next thread
» In other words, signal() has “history” (c.f., condition vars later)
» This “history” is a counter
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
5
Semaphore Types


Semaphores come in two types
Mutex semaphore (or binary semaphore)



Represents single access to a resource
Guarantees mutual exclusion to a critical section
Counting semaphore (or general semaphore)



Represents a resource with many units available, or a
resource that allows certain kinds of unsynchronized
concurrent access (e.g., reading)
Multiple threads can pass the semaphore
Number of threads determined by the semaphore “count”
» mutex has count = 1, counting has count = N
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
6
Using Semaphores

Use is similar to our locks, but semantics are different
struct Semaphore {
int value;
Queue q;
} S;
withdraw (account, amount) {
wait(S);
balance = get_balance(account);
balance = balance – amount;
put_balance(account, balance);
signal(S);
return balance;
}
wait(S);
balance = get_balance(account);
balance = balance – amount;
Threads
block
critical
section
It is undefined which
thread runs after a signal
October 21, 2014
wait(S);
wait(S);
put_balance(account, balance);
signal(S);
…
signal(S);
…
signal(S);
CSE 120 – Lecture 6 – Semaphores and Monitors
7
Semaphores in Nachos
wait (S) {
Disable interrupts;
while (S->value == 0) {
enqueue(S->q, current_thread);
thread_sleep(current_thread);
}
S->value = S->value – 1;
Enable interrupts;
}

thread_sleep() assumes interrupts are disabled




signal (S) {
Disable interrupts;
thread = dequeue(S->q);
thread_start(thread);
S->value = S->value + 1;
Enable interrupts;
}
Note that interrupts are disabled only to enter/leave critical section
How can it sleep with interrupts disabled?
Need to be able to reference current thread
What happens if “while (value !=0)” is an “if (value != 0)”?
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
8
Using Semaphores

We’ve looked at a simple example for using
synchronization


Mutual exclusion while accessing a bank account
Now we’re going to use semaphores to look at more
interesting examples


Readers/Writers
Bounded Buffers
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
9
Readers/Writers Problem

Readers/Writers Problem:



An object is shared among several threads
Some threads only read the object, others only write it
We can allow multiple readers but only one writer
» Let #r be the number of readers, #w be the number of writers
» Safety: (#r ≥ 0) ∧ (0 ≤ #w ≤ 1) ∧ ((#r > 0) ⇒ (#w = 0))


How can we use semaphores to control access to the
object to implement this protocol?
Use three variables



int readcount – number of threads reading object
Semaphore mutex – control access to readcount
Semaphore w_or_r – exclusive writing or reading
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
10
Readers/Writers
// number of readers
int readcount = 0;
// mutual exclusion to readcount
Semaphore mutex = 1;
// exclusive writer or reader
Semaphore w_or_r = 1;
writer {
wait(w_or_r); // lock out readers
Write;
signal(w_or_r); // up for grabs
}
October 21, 2014
reader {
wait(mutex);
// lock readcount
readcount += 1; // one more reader
if (readcount == 1)
wait(w_or_r); // synch w/ writers
signal(mutex); // unlock readcount
Read;
wait(mutex);
// lock readcount
readcount -= 1; // one less reader
if (readcount == 0)
signal(w_or_r); // up for grabs
signal(mutex); // unlock readcount}
}
CSE 120 – Lecture 6 – Semaphores and Monitors
11
Readers/Writers Notes

w_or_r provides mutex between readers and writers



If a writer is writing, where will readers be waiting?
Once a writer exits, all readers can fall through






writer wait/signal, reader wait/signal when readcount goes
from 0 to 1 or from 1 to 0.
Which reader gets to go first?
Is it guaranteed that all readers will fall through?
If readers and writers are waiting, and a writer exits,
who goes first?
Why do readers use mutex?
Why don't writers use mutex?
What if the signal is above “if (readcount == 1)”?
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
12
Bounded Buffer

Problem: There is a set of resource buffers shared by producer
and consumer threads

Producer inserts resources into the buffer set
» Output, disk blocks, memory pages, processes, etc.

Consumer removes resources from the buffer set
» Whatever is generated by the producer

Producer and consumer execute at different rates




No serialization of one behind the other
Tasks are independent (easier to think about)
The buffer set allows each to run without explicit handoff
Safety:


Sequence of consumed values is prefix of sequence of produced
values
If nc is number consumed, np number produced, and N the size of
the buffer, then 0  np  nc  N
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
13
Bounded Buffer (2)


0  np  nc  N and 0  (nc  np)  N  N
Use three semaphores:

empty – count of empty buffers
» Counting semaphore
» empty = (nc  np) + N

full – count of full buffers
» Counting semaphore
» np - nc = full

mutex – mutual exclusion to shared set of buffers
» Binary semaphore
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
14
Bounded Buffer (3)
Semaphore mutex = 1; // mutual exclusion to shared set of buffers
Semaphore empty = N; // count of empty buffers (all empty to start)
Semaphore full = 0;
// count of full buffers (none full to start)
producer {
while (1) {
Produce new resource;
wait(empty); // wait for empty buffer
wait(mutex); // lock buffer list
Add resource to an empty buffer;
signal(mutex); // unlock buffer list
signal(full);
// note a full buffer
}
}
October 21, 2014
consumer {
while (1) {
wait(full);
// wait for a full buffer
wait(mutex); // lock buffer list
Remove resource from a full buffer;
signal(mutex); // unlock buffer list
signal(empty); // note an empty buffer
Consume resource;
}
}
CSE 120 – Lecture 6 – Semaphores and Monitors
15
Bounded Buffer (4)



Why need the mutex at all?
Where are the critical sections?
What has to hold for deadlock to occur?




What happens if operations on mutex and full/empty are switched
around?


empty = 0 and full = 0
(nc  np) + N = 0 and np - nc = 0
N=0
The pattern of signal/wait on full/empty is a common construct often
called an interlock
Producer-Consumer and Bounded Buffer are classic examples of
synchronization problems



The Mating Whale problem in Project 1 is another
You can use semaphores to solve the problem
Use readers/writers and bounded buffer as examples for hw
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
16
Semaphore Questions


Are there any problems that can be solved with
counting semaphores that cannot be solved with
mutex semaphores?
Does it matter which thread is unblocked by a signal
operation?

Hint: consider the following three processes sharing a
semaphore mutex that is initially 1:
while (1) {
wait(mutex);
// in critical section
signal(mutex);
}
October 21, 2014
while (1) {
wait(mutex);
// in critical section
signal(mutex);
}
CSE 120 – Lecture 6 – Semaphores and Monitors
while (1) }
wait(mutex);
// in critical section
signal(mutex);
}
17
Semaphore Summary


Semaphores can be used to solve any of the
traditional synchronization problems
However, they have some drawbacks

They are essentially shared global variables
» Can potentially be accessed anywhere in program


No connection between the semaphore and the data being
controlled by the semaphore
Used both for critical sections (mutual exclusion) and
coordination (scheduling)
» Note that I had to use comments in the code to distinguish


No control or guarantee of proper usage
Sometimes hard to use and prone to bugs

Another approach: Use programming language support
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
18
Monitors

A monitor is a programming language construct that
controls access to shared data



A monitor is a module that encapsulates





Synchronization code added by compiler, enforced at runtime
Why is this an advantage?
Shared data structures
Procedures that operate on the shared data structures
Synchronization between concurrent threads that invoke the
procedures
A monitor protects its data from unstructured access
It guarantees that threads accessing its data through
its procedures interact only in legitimate ways
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
19
Monitor Semantics

A monitor guarantees mutual exclusion


Only one thread can execute any monitor procedure at any
time (the thread is “in the monitor”)
If a second thread invokes a monitor procedure when a first
thread is already executing one, it blocks
» So the monitor has to have a wait queue…


If a thread within a monitor blocks, another one can enter
What are the implications in terms of parallelism in
monitor?
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
20
Account Example
Monitor account {
double balance;
double withdraw(amount) {
balance = balance – amount;
return balance;
}
Threads
block
waiting
to get
into
monitor
withdraw(amount)
balance = balance – amount;
withdraw(amount)
withdraw(amount)
return balance (and exit)
balance = balance – amount
return balance;
}
When first thread exits, another can
enter. Which one is undefined.


balance = balance – amount;
return balance;
Hey, that was easy
But what if a thread wants to wait inside the monitor?
» Such as “mutex(empty)” by reader in bounded buffer?
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
21
Monitors, Monitor Invariants
and Condition Variables


A monitor invariant is a safety property associated with the
monitor, expressed over the monitored variables. It holds
whenever a thread enters or exits the monitor.
A condition variable is associated with a condition needed for a
thread to make progress once it is in the monitor.
Monitor M {
... monitored variables
Condition c;
void enter_mon (...) {
if (extra property not true) wait(c);
do what you have to do
if (extra property true) signal(c);
}
October 21, 2014
waits outside of the monitor's mutex
brings in one thread waiting on condition
CSE 120 – Lecture 6 – Semaphores and Monitors
22
Condition Variables

Condition variables support three operations:

Wait – release monitor lock, wait for C/V to be signaled
» So condition variables have wait queues, too



Signal – wakeup one waiting thread
Broadcast – wakeup all waiting threads
Condition variables are not boolean objects



“if (condition_variable) then” … does not make sense
“if (num_resources == 0) then wait(resources_available)” does
An example will make this more clear
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
23
Monitor Bounded Buffer
Monitor bounded_buffer {
Resource buffer[N];
// Variables for indexing buffer
// monitor invariant involves these vars
Condition not_full; // space in buffer
Condition not_empty; // value in buffer
void put_resource (Resource R) {
if (buffer array is full)
wait(not_full);
Add R to buffer array;
signal(not_empty);
}

Resource get_resource() {
if (buffer array is empty)
wait(not_empty);
Get resource R from buffer array;
signal(not_full);
return R;
}
} // end monitor
What happens if no threads are waiting when signal is called?
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
24
Monitor Queues
Monitor bounded_buffer {
Waiting to enter
Condition not_full;
…other variables…
Condition not_empty;
Waiting on
condition variables
void put_resource () {
…wait(not_full)…
…signal(not_empty)…
}
Resource get_resource () {
…
}
Executing inside
the monitor
}
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
25
Condition Vars != Semaphores

Condition variables != semaphores



Although their operations have the same names, they have
entirely different semantics (such is life, worse yet to come)
However, they each can be used to implement the other
Access to the monitor is controlled by a lock

wait() blocks the calling thread, and gives up the lock
» To call wait, the thread has to be in the monitor (hence has lock)
» Semaphore::wait just blocks the thread on the queue

signal() causes a waiting thread to wake up
» If there is no waiting thread, the signal is lost
» Semaphore::signal increases the semaphore count, allowing
future entry even if no thread is waiting
» Condition variables have no history
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
26
Signal Semantics

There are two flavors of monitors that differ in the
scheduling semantics of signal()

Hoare monitors (original)
» signal() immediately switches from the caller to a waiting thread
» The condition that the waiter was anticipating is guaranteed to
hold when waiter executes
» Signaler must restore monitor invariants before signaling

Mesa monitors (Mesa, Java)
» signal() places a waiter on the ready queue, but signaler
continues inside monitor
» Condition is not necessarily true when waiter runs again


October 21, 2014
Returning from wait() is only a hint that something changed
Must recheck conditional case
CSE 120 – Lecture 6 – Semaphores and Monitors
27
Hoare vs. Mesa Monitors

Hoare
if (empty)
wait(condition);

Mesa
while (empty)
wait(condition);

Tradeoffs

Mesa monitors easier to use, more efficient
» Fewer context switches, easy to support broadcast

Hoare monitors leave less to chance
» Easier to reason about the program
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
28
Monitor Readers and Writers
Using Mesa monitor semantics.

Will have four methods: StartRead, StartWrite,
EndRead and EndWrite

Monitored data: nr (number of readers) and nw
(number of writers) with the monitor invariant
(nr ≥ 0) ∧ (0 ≤ nw ≤ 1) ∧ ((nr > 0) ⇒ (nw = 0))
 Two conditions:


canRead: nw = 0
canWrite: (nr = 0) ∧ (nw = 0)
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
29
Monitor Readers and Writers

Write with just wait() (will be safe, maybe not live why?)
Monitor RW {
int nr = 0, nw = 0;
Condition canRead, canWrite;
void StartWrite {
while (nr != 0 || nw != 0) do wait(canWrite);
nw++;
}
void StartRead () {
while (nw != 0) do wait(canRead);
nr++;
}
void EndWrite () {
nw--;
}
} // end monitor
void EndRead () {
nr--;
}
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
30
Monitor Readers and Writers

add signal() and broadcast()
Monitor RW {
int nr = 0, nw = 0;
Condition canRead, canWrite;
void StartRead () {
while (nw != 0) do wait(canRead);
nr++;
}
can we put a signal here?
void EndRead () {
nr--;
if (nr == 0) signal(canWrite);
}
October 21, 2014
void StartWrite () {
while (nr != 0 || nw != 0) do wait(canWrite);
nw++;
}
can we put a signal here?
void EndWrite () {
nw--;
broadcast(canRead);
signal(canWrite);
}
} // end monitor
CSE 120 – Lecture 6 – Semaphores and Monitors
31
Monitor Readers and Writers


Is there any priority between readers and writers?
What if you wanted to ensure that a waiting writer
would have priority over new readers?
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
32
Condition Vars & Locks

Condition variables are also used without monitors in
conjunction with blocking locks


A monitor is “just like” a module whose state includes
a condition variable and a lock


Difference is syntactic; with monitors, compiler adds the code
It is “just as if” each procedure in the module calls
acquire() on entry and release() on exit


This is what you are implementing in Project 1
But can be done anywhere in procedure, at finer granularity
With condition variables, the module methods may
wait and signal on independent conditions
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
33
Using Cond Vars & Locks


Alternation of two threads (ping-pong)
Each executes the following:
Lock lock;
Condition cond;
Must acquire lock before you can
wait (similar to needing interrupts
disabled to call Sleep in Nachos)
void ping_pong () {
acquire(lock);
while (1) {
printf(“ping or pong\n”);
signal(cond, lock);
wait(cond, lock);
}
release(lock);
}
October 21, 2014
Wait atomically releases lock
and blocks until signal()
Wait atomically acquires lock
before it returns
CSE 120 – Lecture 6 – Semaphores and Monitors
34
Monitors and Java

A lock and condition variable are in every Java object


No explicit classes for locks or condition variables
Every object is/has a monitor


At most one thread can be inside an object’s monitor
A thread enters an object’s monitor by
» Executing a method declared “synchronized”

Can mix synchronized/unsynchronized methods in same class
» Executing the body of a “synchronized” statement



Supports finer-grained locking than an entire procedure
Identical to the Modula-2 “LOCK (m) DO” construct
The compiler generates code to acquire the object’s lock at
the start of the method and release it just before returning
» The lock itself is implicit, programmers do not worry about it
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
35
Monitors and Java

Every object can be treated as a condition variable


Half of Object’s methods are for synchronization!
Take a look at the Java Object class:



Object::wait(*) is Condition::wait()
Object::notify() is Condition::signal()
Object::notifyAll() is Condition::broadcast()
April 21, 2009
CSE 120 – Lecture 6 – Semaphores and Monitors
36
Summary

Semaphores




wait()/signal() implement blocking mutual exclusion
Also used as atomic counters (counting semaphores)
Can be inconvenient to use
Monitors

Synchronizes execution within procedures that manipulate
encapsulated data shared among procedures
» Only one thread can execute within a monitor at a time


Relies upon high-level language support
Condition variables


Used by threads as a synchronization point to wait for events
Inside monitors, or outside with locks
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
37
Next time…

Read Chapters 6.5, 7.1, 7.6, 7.7
October 21, 2014
CSE 120 – Lecture 6 – Semaphores and Monitors
38