0% found this document useful (0 votes)
26 views

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

Uploaded by

usman
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
26 views

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

Uploaded by

usman
Copyright
© © All Rights Reserved
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