0% found this document useful (0 votes)
49 views12 pages

8-Semaphores, Monitors, and Message Passing: Producer/Consumer With Sleep/Wakeup

Uploaded by

usman
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
49 views12 pages

8-Semaphores, Monitors, and Message Passing: Producer/Consumer With Sleep/Wakeup

Uploaded by

usman
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

8-Semaphores, Monitors,

and Message Passing

Mark Handley

Producer/Consumer with Sleep/Wakeup


(Fatally flawed due to Race Condition)
#define N 100 /* no. of slots in buffer */
int count = 0; /* no. of items in buffer */
void producer() {
while (TRUE) {
int item = produce_item(); /* generate next item */
if (count == N) sleep(); /* go to sleep if buffer is full */
insert_item(item); /* store item in buffer */
count++; /* increment count of items in buffer */
if (count == 1) wakeup(consumer); /* if buffer was empty, wake consumer */
}
}
void consumer() {
while (TRUE) {
if (count == 0) sleep(); /* sleep if nothing in buffer */
item = remove_item(); /* take item from buffer */
count--; /* decrement count of items in buffer*/
if (count == N-1) wakeup(producer); /* if buffer was full, wake producer */
consume_item(item);
}
}

1
Semaphores (Dykstra, 1965)
int s;
 wait
waitisissometimes
sometimes
wait(s) { known
knownas asdown,
down,ororPP
while (s == 0) { /* no-op */ }  signal
signalisissometimes
sometimes
s--; known
knownas asup,
up,or
orVV
}

signal(s) {
s++;
}

 wait must be implemented as an atomic action


 typically by the CPU disabling interrupts, and using a TSL
instruction on a multiprocessor machine.

Using Semaphores

A semaphore can be used to provide Mutual Exclusion:

int mutex = 1;

while (TRUE) {
wait(mutex);
critical section
signal(mutex);
non-critical section This is known
} as a mutex lock

2
Using Semaphores
A semaphore can be used to ensure that one section of code is
run before another section, even when they’re in different
processes.

 Process 1:  Process 2:

int signal = 0;

First Section wait(sync)


signal(synch); Second Section

Implementing Semaphores
typedef
typedefstruct
struct{{
int value;
int value;
struct
structprocess
process*L;
*L;
}}semaphore;
semaphore;
void
voidwait(semaphore
wait(semaphoreS) S){{ void
voidsignal(semaphore
signal(semaphoreS) S){{
S.value--;
S.value--; S.value++;
S.value++;
ifif(S.value
(S.value<<0)0){{ ifif(S.value
(S.value<=
<=0)0){{
add
addthis
thisprocess
processtotoS.L;
S.L; remove
removeaaprocess
processPPfrom
fromS.L;
S.L;
sleep();
sleep(); wakeup(P);
wakeup(P);
}} }}
}} }}

 wait and signal must be atomic.

3
Implementing a Mutex using TSL
User-space thread implementation
mutex_lock:
TSL Register, Mutex // copy mutex to Register, set Mutex to 1
CMP Register, 0 // test if mutex was zero
JZE ok // it is was, mutex wasn’t locked, so return
CALL thread_yield // mutex is busy: schedule another thread
JMP mutex_lock // loop and try again
ok: RTN // return to caller, and enter critical section

mutex_unlock:
MOVE Mutex, 0 // clear the lock
RTN // return to caller

Producer/Consumer using Semaphores


semaphore
semaphoremutex
mutex==1; 1; /*/*controls
controlsaccess
accessto tocritical
criticalsection*/
section*/
semaphore
semaphoreempty
empty==N; N; /*/*counts
countsempty
emptybuffer
bufferslots
slots*/*/
semaphore
semaphorefull
full==0;
0; /*/*counts
countsfull
fullbuffer
bufferslots
slots*/*/

void
voidproducer()
producer(){{ void
voidconsumer()
consumer(){{
while
while(TRUE)
(TRUE){{ while
while(TRUE)
(TRUE){{
int
intitem
item==produce_item();
produce_item(); wait(&full);
wait(&full);
wait(&empty);
wait(&empty); wait(&mutex);
wait(&mutex);
wait(&mutex);
wait(&mutex); item
item==remove_item();
remove_item();
insert_item(item);
insert_item(item); signal(&mutex);
signal(&mutex);
signal(&mutex);
signal(&mutex); signal(&empty);
signal(&empty);
signal(&full);
signal(&full); consume_item(item);
consume_item(item);
}} }}
}} }}

Two different uses of semaphores: mutex is used for mutual


exclusion. full and empty are used as blocking counters.

4
Problems with Semaphores (2)

 It’s really hard to use semaphores correctly.


 Easy to mix up wait() and signal().
 Easy to omit signal().
 Especially when handling errors.

 Such bugs may be hard to find, because they only


show up under certain race conditions.

Monitors
 Hoare’s response to Dijkstra’s semaphores
 Higher-level
 Structured
 Monitors encapsulate data structures that are not
externally accessible
 Mutual exclusive access to data structure enforced
by compiler or language run-time.

5
Monitors
 Monitors are a high-level synchronization primitive:
monitor monitor-name
{
Key points:
shared variable declarations
• Shared variables are only
procedure P1 ( ... ) {
... accessible through the
} monitor’s procedures.
...
procedure Pn ( ... ) { • The language performs
...
} locking so that only one
process can be running
{ any monitor procedure at
initialization code
} a time.
}

Monitor Condition Variables

 A monitor can have condition variables that can be


used to add additional synchronization behaviour
beyond basic mutual exclusion.
 condition x;
 The only operations that can be performed on
condition variables are wait() and signal()
 x.wait() behaves like sleep()
 x.signal() behaves like wakeup(), and wakes up
the process sleeping on x.

6
Producer/Consumer using a Monitor
(pseudo code, not real C/Java)

monitor ProducerConsumer { void Producer() {


condition full, empty; while (TRUE) {
int count=0; int item = produce_item();
void insert(int item) { ProducerConsumer.insert(item);
if (count == N) full.wait(); }
insert_item(item); }
count++;
if (count == 1) empty.signal(); void Consumer() {
} while(TRUE) {
int remove() { int item = ProducerConsumer.remove();
if (count == 0) empty.wait(); consume_item(item);
int item = remove_item; }
count--; }
if (count == N-1) full.signal();
}
}

Monitors in Java
 A monitor can easily be implemented in Java:
 A class in which all the methods are synchronized.
 Guarantees mutual exclusion.
 All instance and class variables need to be private or
protected.

 No explicit condition variables, but:


 Use wait() to put thread to sleep, allowing other threads
to enter the monitor.
 Use notify() to wake up a thread that’s waiting on the
monitor’s thread queue.

7
Condition Synchronisation in Java

 Thread enters monitor when it acquires mutual


exclusion lock of monitor
 Thread exits monitor when releasing lock
 Wait causes thread to exit monitor

Monitor
data
Wait()

Notify()

Semaphore as a Java Monitor

class Semaphore {
private int value_;
Semaphore (int initial) {
value_=initial;
}
public synchronised down() {
while (value_==0) wait();
--value;
}
public synchronised up() {
++value_;
notify();
}
}

8
Condition Synchronisation in Java
 Although Java has no condition variables, they can be
emulated via a loop:

public synchronized void action()


throws InterruptedException
{
while (! condition) wait();
do some stuff
notifyAll();
}

 Loop re-tests condition to make sure that it is valid when the


thread re-enters the monitor.

Problems with Semaphores


 Semaphores, Petersons’s algorithm, and TSL all assume
processes share memory.
 But processes are normally protected from each other.

 Three solutions:
 Semaphores may be stored in the kernel, and only accessed
through system calls.
 Use shared-memory segments (available on Unix and
Windows).
 Use a file.

 None of these are nearly as efficient as synchronization


between threads in the same process.

9
Problems with Monitors

 Monitors are a programming language construct.


 Not only do they need shared memory, but they’re
not even available in many languages.
 Eg. C, C++.

Message Passing

 If two process cannot share memory, then none of the


solutions described can work.
 Then you need to use Message Passing.
 This is the general term for a range of different
IPC mechanisms.
 Two primitives:
send(destination, &message);
receive(source, &message);
 These are system calls, not language constructs

10
Producer/Consumer with Messages
#define N 100 #define N 100

void producer() { void consumer() {


int item; int item, i;
message m; message m;
while (TRUE) { for (i=0; i<N; i++)
item = produce_item(); send(producer, &m);
receive(consumer, &m); while (TRUE) {
build_message(&m, item); receive(producer, &m);
send(consumer, &m); item = extract_item(&m);
} send(producer, &m);
}
consume_item(&m);
}
}

Design Issues with Message Passing


 If processes are on different machines, messages can be lost.
 Use acknowledgements, and resend if a message is lost.
 Use sequence numbers to distinguish retransmissions.

 If processes are on the same machine, efficiency is key


concern.
 Message passing involves context switching and copying
the messages.
 How to identify the recipient?
 Directly
 Use a mailbox.

 Buffered mailbox vs unbuffered rendezvous.

11
Barriers

 Use of a barrier
 processes approaching a barrier
 all processes but one blocked at barrier
 last process arrives, all are let through

Summary
Many techniques for inter-process synchronization:
Semaphores:
 low level, efficient, shared memory, hard to use right.

Monitors:
 high level language construct, shared memory.

Message Passing:
 asynchronous, low-level, no shared memory.

 Next: Deadlocks.

12

You might also like