Practical Session 7

Operating Systems, 112
Practical Session 7
Synchronization, part 3
Monitors, classical sync. problems
1
Monitor
• Monitor – a synchronization primitive.
• A monitor is a collection of procedures, variables and
data structures, grouped together.
• Mutual Exclusion – only one process can be active
within a monitor at any given time.
• Programming language construct!
The compiler of the language will know that monitors
procedures are different than other procedures, and
will treat them differently. That means that the
compiler is in charge of the mutual exclusion
implementation.
Java synchronized methods
2
A quick recap
Condition variables
• A way for processes to block when they can’t continue.
• Despite its name, it is used to indicate an event and not as a
regular valued variable. A CV is not a counter!
• Two operations: wait, signal.
Wait: causes the process to block, and allows entry of other
threads to the monitor.
Signal:
More than just one alternative:
1.
2.
Hoare type monitors: The signaler yields the monitor to the
released thread. Signal will be the last operation within the monitor,
which wakes up waiting processes (waiting on the same variable).
This is not true for Java.
Mesa type monitors: The signaling process is allowed to continue.
3
The Sleeping Barber
Write a solution to the sleeping barber
problem using monitors and condition variables.
Reminder, the sleeping barber:
1.
2.
3.
4.
The barber cuts peoples hair in his shop, which has 2 doors – entrance
and exit. When people are in his shop, he gives them a hair cut, one at a
time. When none are in his shop, he sleeps on his chair.
When a customer arrives and finds the barber sleeping, he awakens him
and sits in the barber’s chair to receive his haircut. After the cut is done,
the barber sees the customer out through the exit door.
If the barber is busy when a customer arrives, the customer waits in one
of the chairs in the shop. If all are occupied, he goes away.
After serving a customer the barber looks to see if any are waiting and if
so proceeds to serve one of them. Otherwise, he sleeps again in his
chair.
4
The Sleeping Barber
barbershop: monitor
waiting : integer := 0;
customers : condition;
barber : condition;
% customers waiting for haircut
% used by barber, wait for a customer
% used by customer, wait for barber
procedure seek-customer( )
begin
if waiting==0 then
WAIT (customers);
cut-hair();
waiting = waiting-1;
SIGNAL (barber);
end seek-customer;
% called by the barber
% sleeps if no customers
% one less customer waiting
% free a waiting customer
5
The Sleeping Barber
procedure get-haircut( ) % called by a customer
begin
% is there a free chair to sit and wait?
% if no free chairs just go away
if waiting < chairs then
{
waiting = waiting+1;
SIGNAL (customers)
WAIT (barber);
}
end get-haircut;
end barbershop;
% one more customer waiting
% if the barber is asleep
% wait for turn with the barber
% End of monitor
6
Java and monitors
Write a code snippet in Java which will enforce a
FIFO waking order.
7
Java and monitors
class WaitObject {
boolean released = false;
//this flag avoids race!!!
synchronized void doWait() {
while (! released) {
try {
wait();
}
catch (InterruptedException ie) {} //ignore it
}
}
synchronized void doNotify(){
if (! released){
released = true;
notify();
}
}
}
8
Java and monitors
class CriticalSection {
private Vector _waiting;
private boolean _busy;
// critical section that preserves FIFO
// wait list
// someone in critical section
public CriticalSection() {
// constructor
_waiting = new Vector();
// create wait list
_busy = false;
// no one is in the CS now.
}
public void enter() {
WaitObject my_lock = null;
synchronized(this){
if (! _busy) {
_busy = true;
return;
}else {
my_lock = new WaitObject();
// create my unique lock
_waiting.add(my_lock);
// add to waiting list
}
}
my_lock.doWait();
// wait on lock
}
9
Java and monitors
public synchronized void leave() {
if (_waiting.size() > 0) {
// someone is waiting
WaitObject o = (WaitObject)_waiting.elementAt(0);
_waiting.removeElementAt(0);
o.doNotify();
} else {
_busy = false;
}
}
}
10
Java and monitors
Does this code guarantee a FIFO waking order
which is equivalent to the order in which threads
reached the critical section entrance?
NO!
public void enter() {
WaitObject my_lock = null;
synchronized(this){
…
}
my_lock.doWait();
}
What happens when multiple
threads attempt to enter at the
same time?
It does, however, guarantee a waking order on
those threads already sleeping.
11
Hoare monitors, Moed A 2010
Monitor ProducerConsumer
1.
condition full, empty
2.
integer count initially 0
3.
procedure insert(item: integer)
4.
begin
5.
if count=N then wait(full)
6.
insert_item(item)
7.
count=count+1
8.
if count > 0 then signal(empty)
9.
end
10.
procedure remove: integer
11.
begin
12.
if count=0 then wait(empty)
13.
remove=remove_item
14.
count=count-1
15.
if count < N then signal(full)
16.
end
end Monitor
12
Hoare monitors, Moed A 2010
Recall that a monitor is a Hoare typed monitor if the following
conditions hold:
1. Whenever a ‘signal’ is received by a thread t it is guaranteed
that thread t will be the next thread to enter the monitor
2. ‘signal’ is the last action taken within the monitor and a
thread executing it will immediately release the monitor
afterward
Consider the above implementation for the producer consumer
problem with N items and prove or disprove the following claim:
The above implementation is correct even if used with non Hoare
typed monitor
13
Hoare monitors, Moed A 2010
The following scenario contradicts the above claim:
Assume an N sized buffer, already full. Now, consider a producer
p1 attempting to add a new item. Reaching line 5 p1 must wait
for some consumer to remove an item before it may continue.
Now, we introduce a consumer c1 which successfully executes
the remove function – including line 15 (signal). However, unlike
a regular Hoare typed monitor, instead of having p1 take control
of the monitor a new producer p2 is added. The monitor is taken
by p2 which executes the entire insert() function. Finally, p1 is
granted CPU time and continues its run (line 6 to the end of the
function).
Result: an extra item is added to the already full buffer
14
The one-way tunnel problem
• One-way tunnel
• Allows any number of processes in the
same direction
• If there is traffic in the opposite direction –
have to wait
• A special case of readers/writers
15
Operating Systems, 2011, Danny Hendler &
Amnon Meisels
The one way tunnel (exam 2004)
The one way tunnel solution:
int
Semaphore
Semaphore
count[2];
mutex=1, busy=1;
waiting[2]={1,1};
void arrive(int direction){
down(&waiting[direction]);
down(&mutex);
count[direction]+=1;
if (count[direction]==1){
up(&mutex);
down(&busy);
} else
up(&mutex);
up(&waiting[direction]);
}
void leave(int direction){
down(&mutex);
count[direction]-=1;
if (count[direction]==0){
up(&busy);
}
up(&mutex);
The one way tunnel (exam 2004)
1. Add changes to the one way tunnel solution so that
vehicles from direction “0” are always preferred.
Vehicles from direction “1” will only enter the tunnel if
no vehicles at “0” are waiting.
2. Add changes to the one way tunnel solution so that
there will be no starvation. If vehicles are present on
both “0” and “1” they will take alternate turns in
entering the tunnel. When there are vehicles coming
from only one direction, they can pass through with no
limitations.
Notes:
• you may only use integers and binary semaphores.
• You may change the solution for a single direction at a time.
The one way tunnel (exam 2004)
1.
Code for direction 0:
Semaphore
new_mutex=1;
void arrive() {
down(&waiting[0]);
down(&mutex);
count[0]++;
if(count[0] == 1) {
up(&mutex);
down(&new_mutex);
down(&busy);
up(&new_mutex);
{ else
up(&mutex);
up(waiting[0]);
}
Code for direction 1:
void arrive(int direction) {
down(waiting[1]);
down(&new_mutex);
down(&mutex);
count[1]++;
if(count[1] == 1) {
up(&mutex);
up(waiting[1]);
down(busy);
{ else {
up(&mutex);
up(waiting[1]);
{
up(&new_mutex);
}
The one way tunnel (exam 2004)
2.
For both directions:
void arrive(int direction) {
down(waiting[direction]);
down(new_mutex);
down(mutex);
count[direction]++;
if(count[direction] == 1) {
up(mutex);
up(waiting[direction]);
down(busy);
{ else {
up(mutex);
up(waiting[direction]);
{
up(new_mutex);
}
void leave(int direction){
down(&mutex);
count[direction]-=1;
if (count[direction]==0){
up(&busy);
}
up(&mutex);
Assuming semaphore fairness (new_mutex)
One way, convoy (midterm 2008)
In the following question you must implement a solution to the convoy
problem using only semaphores (and regular variables). In this
problem, each thread represent a vehicle. The vehicles must go
through a one way tunnel, but unlike the tunnel problem, here
vehicles may only cross the tunnel in groups of exactly 5 (all in the
same direction). A group of another 5 vehicles (from the same or
opposite direction) may cross the tunnel again, only after the previous
group of 5 vehicles comes out of it.
The general code structure is as follows:
 Variable Declaration
 PrepareToCross(int direction)
 CROSS
 DoneWithCrossing(int direction)
One way, convoy (midterm 2008)
Implement PrepareToCross() and DoneWithCrossing(). For your
implementation you may only use semaphores (counting or binary)
and regular variables. No busy-waiting is allowed.
We say a thread is passing the tunnel if it completed its call to
PrepareToCross and haven’t called DoneWithCrossing or if it is still in
PrepareToCross but is no longer waiting on a semaphore, and when it
will receive CPU time it may complete the procedure without waiting.
Your implementation must satisfy the following conditions:
1.
2.
3.
Mutual Exclusion – threads from different direction may never be in
the tunnel at the same time.
Threads may only cross in groups of 5. When the first is leaving
PrepareToCross, there are exactly 4 others which are passing the
tunnel as well.
Progress – Whenever there are 5 (or more) threads from the same
direction waiting to cross the tunnel, then eventually, they will.
One way, convoy (midterm 2008)
We will use the following:
Counting
Counting
Binary
Binary
int
int
Semaphore waitingToCross[]={5,5}
Semaphore barrier[]={0,0}
Semaphore busy=1
Semaphore mutex=1
waitingCount[]={0,0}
passed=0
One way, convoy (midterm 2008)
PrepareToCross(int i){
down(&waitingToCross[i]);
down(&mutex);
waitingCount[i]++;
If (waitingCount[i]<5){
up(&mutex);
down(&barrier[i]);
} else {
waitingCount[i]=0;
up(&mutex);
down(&busy);
for (int k=0; k<4; k++)
up(&barrier[i]);
}
up(&waitingToCross[i]);
}
DoneWithCrossing(int i){
down(&mutex);
passed++;
if (passed==5){
passed=0;
up(&busy);
}
up(&mutex);
}
A quick recap
• Message passing
• Used on distributed systems (when there is no shared
memory).
• Uses send(), and receive() system calls.
• Introduces a new set of problems, such as
acknowledgments, sequencing, addressing,
authentication, etc’…
24
Reader/Writer problem with MP
Write a solution to the reader/writer problem
using Message Passing.
Assume the following:
1.
2.
3.
4.
Three groups of processes: readers, writer, manager.
Multiple readers may access the DB simultaneously.
A writer needs exclusive access to the DB.
Readers have preference.
25
Reader/Writer problem with MP
Reader:
while (true){
SEND (manager, start_read);
RECEIVE (manager, msg);
% wait for confirmation
read_db();
SEND (manager, end_read);
use_data();
}
Writer:
while (true){
generate_data();
SEND (manager, start_write);
RECEIVE (manager, msg);
% wait for confirmation
write_to_db();
SEND (manager, end_write);
}
26
Reader/Writer problem with MP
Manager:
int readers_count=0;
% number of readers accessing DB
boolean writing=false;
% writing flag
Message msg;
Queue readQ, writeQ;
% Queues for waiting readers and writers
ProcessID src;
% pid
while (true){
src = RECEIVE(msg);
switch msg.type{
case (start_read):
if (not writing){
send(src, ok);
readers_count++;
} else
readQ.add(src);
27
Reader/Writer problem with MP
case (end_read):
readers_count--;
if (readers_count==0 && not writeQ.empty){
src=writeQ.remove;
SEND (src, ok);
writing = true;
}
case (start_write):
if (readers_count==0 && not writing){
SEND (src, ok);
writing = true;
} else
writeQ.add(src);
28
Reader/Writer problem with MP
case (end_write):
writing = false;
if (readQ.empty && not writeQ.empty){
src = writeQ.remove;
SEND(src, ok);
writing = true;
} else {
while (not readQ.empty){
src = readQ.remove;
send(src, ok);
readers_count++;
}
}
} % switch
} % while
29
The sleeping barber problem
• Barber shop - one service provider; many customers
• A finite waiting queue
• One customer is served at a time
• Service provider, barber, sleeps when no customers
are waiting
• Customer leaves if shop is full
• Customer sleeps while waiting in queue
30
The sleeping barber
#define CHAIRS 5
semaphore customers = 0; // number of waiting customers
Semaphore barbers = 0; // number of available barbers: either 0 or 1
Semaphore mutex = 1; // mutex for accessing ‘waiting’
Semaphore synch = 0; // synchronizing the service operation
int waiting = 0; // copy of customers for reading
void barber(void) {
while(TRUE)
{
down(customers); // block if no customers
down(mutex); // access to ‘waiting’
waiting = waiting - 1;
up(barbers); // barber is in..
up(mutex); // release ‘waiting’
cut_hair();
down(synch) //wait for customer to leave }
}
31
The sleeping barber
void customer(void) {
down(mutex); // access to `waiting’
if(waiting < CHAIRS)
{
waiting = waiting + 1; // increment waiting
up(customers); // wake up barber
up(mutex); // release ‘waiting’
down(barbers); // go to sleep if barbers=0
get_haircut();
up(sync); //synchronize service
}
else {
up(mutex);
}
/* shop full .. leave */
}
32