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

semaphores

Uploaded by

Huỳnh Minh An
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

semaphores

Uploaded by

Huỳnh Minh An
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 23

Semaphores

and other Wait-and-Signal mechanisms

Carsten Griwodz
University of Oslo
(including slides by Otto Anshus and Kai Li)

Critical Regions

Four conditions to provide mutual exclusion


1. No two threads simultaneously in critical region
2. No assumptions made about speeds or numbers of CPUs
3. No thread running outside its critical region may block another thread
4. No thread must wait forever to enter its critical region
Critical Regions

Mutual exclusion using critical regions

Recall Processes and Threads

• Process • Thread
– Address space – Program counter
– Program text and data – Registers
– Open files – Stack
– Child process IDs
– Alarms
– Signal handlers
– Accounting information • Implemented in kernel or in
user space
• Implemented in kernel • Threads are the scheduled
entities
Producer-Consumer Problem

• Main problem description


– Two threads
– Different actions in the critical region
– The consumer can not enter the CR more often than
the producer
• Two sub-problems
– Unbounded PCP: the producer can enter the CR as
often as it wants
– Bounded PCP: the producer can enter the CR only N
times more often than the consumer

Unbounded PCP

PUT (msg) GET (buf) Rules for the queue Q:


•No Get when empty
Consumer
Producer •Q shared, so must have
mutex between Put and Get
Recall Mutexes

• Can be acquired and released


– Only one thread can hold one mutex at a time
– A second thread trying to acquire must wait

• Mutexes
– Can be implemented using busy waiting
– Simpler with advanced atomic operations
• Disable interrupts, TSL, XCHG, …
– Still many approaches using busy waiting
– Better implemented using system calls block & unblock

Bounded PCP

out

in
Rules for the buffer B:
Capacity: N •No Get when empty
•No Put when full
GET (buf)
PUT (msg) •B shared, so must have
mutex between Put and
Get
Producer Consumer
Mutex Solution
Put(msg) { Get(msg) {
acquire(mutex); acquire(mutex);
<put> while(empty) {
release(mutex); release(mutex);
Unbounded PCP } acquire(mutex);
}
<get>
release(mutex);
Busy waiting }

Put(msg) { Get(msg) {
acquire(mutex); acquire(mutex);
while(full) { while(empty) {
release(mutex); release(mutex);
Bounded PCP acquire(mutex); acquire(mutex);
} }
<put> <get>
release(mutex); release(mutex);
} }

Two Kinds of Synchronization

LOCK is initially OPEN

Acquire (id); Acquire (id);


MUTEX <CR> <CR>

Acquire will let first Release (id); Release (id);


caller through, and
then block next until
Release

LOCK is initially CLOSED


CONDITION
SYNCHRONIZATION Acquire (id); Release (id);

Acquire will
block first caller
until Release SIGNAL
Sleep and Wakeup / Signal and Wait

• Wait (cond) • Signal (cond)


– Insert(caller, – Unblock first in
cond_queue) cond_queue, or just
– Block this thread return if empty

No counting, unused signals are ignored


Wait is atomic

Unbounded PCP using Signal and Wait


Q
Rules for the queue Q:
•No Get when empty
GET (buf):
PUT (msg): •Q shared, so must have mutex
between Put and Get
Producer Consumer

while(1) { while(1) {
<process> if(empty)
acquire(mutex); wait(cond);
<insert> acquire(mutex);
release(mutex); <remove>
signal(cond); release(mutex);
} <process>
}
Unbounded PCP using Signal and Wait
Q

GET (buf):
PUT (msg):

Producer Consumer

while(1) { while(1) {
<process> if(empty)
acquire(mutex); wait(cond);
<insert> acquire(mutex);
release(mutex); <remove>
signal(cond); release(mutex);
} <process>
}
Lost signal

Unbounded PCP using Signal and Wait


Q

GET (buf):
PUT (msg):

Producer Consumer

while(1) { while(1) {
<process> acquire(mutex);
acquire(mutex); while(empty) {
<insert> wait(cond);
signal(cond); release(mutex);
release(mutex); acquire(mutex);
} Producer can’t enter }
<remove>
release(mutex);
<process>
}
Unbounded PCP using Signal and Wait
Q

GET (buf):
PUT (msg):

Producer Consumer

while(1) { while(1) {
<process> acquire(mutex);
acquire(mutex); while(empty) {
<insert> release(mutex);
signal(cond); wait(cond);
release(mutex); acquire(mutex);
} }
<remove>
Lost signal release(mutex);
<process>
}

Threads wait for …

• Access to a critical region


– Mutex
– Semaphore
• A condition to be fulfilled
– Condition variable
– Barrier
– Semaphore
Semaphores

Semaphores (Dijkstra, 1965)


prolaag verhoog
• Down or Wait or “P” • Up or Signal or “V”
– Atomic – Atomic
– Decrement semaphore – Increment semaphore by 1
value by 1 – Wake up a waiting thread if
– Block if not positive any

P(s) { V(s) {
if (--s < 0) if (++s <= 0)
Block(s); Unblock(s);
} }

Can get negative s: counts number of waiting threads

s is NOT accessible through other means than calling P and V


Semaphores w/Busy Wait

P(s): V(s):
while (s <= 0) {};
s++;
s--;

ATOMIC

• Starvation possible?

• Does it matter in practice?

The Structure of a Semaphore

s integer Threads waiting to get return after calling P (s) when s was <=0

sem_wait_queue

V (s) P (s)
Unblock Block
(FIFO is fair)
+1 -1

•Atomic: Disable interrupts


•Atomic: P() and V() as System calls
•Atomic: Entry-Exit protocols
Using Semaphores
“The Signal” “The Mutex”
s := 0; s := 1;
A B
P (s); V (s); P (s); P (s);
<CR> <CR>
V(s); V(s);

A blocks until B says V One thread gets in, next


blocks until V is executed

s := 11; “The Team”

NB: remember to set the P(s); P(s);


<max 11> <max 11>
initial semaphore value! V(s); V(s);

Up to 11 threads can pass P, the


ninth will block until V is said by
one of the eight already in there

Simple to debug?

A x := 0; B

P (x); y := 0;
P (y);
….. …..

V (y); V (x);

What will happen?

THEY ARE FOREVER WAITING FOR EACH OTHERS SIGNAL


(“No milk”)
Examples

Unbounded PCP using Semaphores


Q
Rules for the queue Q: One semaphore for each
condition we must wait
•No Get when empty for to become TRUE:
GET (buf):
PUT (msg): •Q shared, so must have •Q empty: nonempty:=0;
mutex between Put and
Producer Consumer
Get •Q mutex: mutex:=1;

PUT (msg): GET (buf): •Is Mutex needed when only 1 P and 1 C?
P(mutex); P(nonempty); •PUT at one end, GET at other end
<insert> P(mutex);
V(mutex); <remove>
V(nonempty); V(mutex);
Bounded PCP using Semaphores
Rules for the buffer B: One semaphore for each
out
condition we must wait
B •No Get when empty for to become TRUE:
in •No Put when full •B empty: nonempty:=0;
Capacity: N •B shared, so must have •B full: nonfull:=N
mutex between Put and
Get •B mutex: mutex:=1;
GET (buf):
PUT (msg):

Producer Consumer

PUT (msg): GET (buf): •PUT at one end, GET at other end
P(nonfull); P(nonempty);
P(mutex); P(mutex);
<insert> <remove>
V(mutex); V(mutex);
V(nonempty); V(nonfull);

Dining Philosophers Problem

• Five philosopher
• Five dishes
• Five forks
• But a philosopher needs two forks for eating

• Usually the philosophers think, when they are hungry


the try to eat
• How to prevent all philosophers from starving
Dining Philosophers
•Each: 2 forks to eat s state
• Free
•5 philosophers: 10 forks to let all
eat concurrently
i+1
i i+1 •5 forks: 2 can eat concurrently
i
Mutex on whole table: P(mutex); Ti
•1 can eat at a time eat;
V(mutex);

Get L; Get R; P(s(i)); Ti


•Deadlock possible P(s(i+1));
eat;
V(s(i+1));
S(i) = 1 initially
V(s(i));

Ti
Get L; Get R if free else Put L;
•Starvation possible

Dining Philosophers
To avoid starvation they could look after each other:
s state
•Entry: If L and R is not eating we can •Thinking
•Eating
•Exit: If L (R) wants to eat and L.L (R.R) is not
i+1 •Want
i i+1 eating we start him eating
S() = 0 initially
i P(mutex);
state(i):=Want;
if (state(i-1) !=Eating AND state(i+1) != Eating)
Ti {/*Safe to eat*/
While (1) { state(i):=Eating;
V(s(i)); /*Because */ }
<think> V(mutex);
ENTRY; P(s(i)); /* Init was 0!!
<eat> We or neighbor must say V(i) to us!*/
EXIT;
} P(mutex);
state(i):=Thinking;
if (state(i-1)=Want AND state(i-2)
!=Eating)
{
state(i-1):=Eating;
V(s(i-1)); /*Start Left neighbor*/
}
/*Analogue for Right neighbor*/
V(mutex);
Dining Philosophers
s
S(i) = 1 initially

i+1 T1, T2, T3, T4:


i i+1 P(s(i)):
i •Remove the danger of P(s(i+1));
circular waiting (deadlock) <eat>
Can we in a simple way do better V(s(i+1));
•T1-T4: Get L; Get R;
than this one? V(s(i));
•T5: Get R; Get L;
Get L; Get R; P(s(i));
•Deadlock possible P(s(i+1)); T5
eat; P(s(1));
V(s(i+1)); P(s(5));
V(s(i)); <eat>
V(s(5));
V(s((1));
•Non-symmetric solution. Still
quite elegant

Readers and Writers Problem

• Several threads
• Shared data in a critical region

• Sometimes a thread wants to read the data


• Sometimes a thread wants to change the data

• Readers can enter a critical region together


• Writers can not enter a critical region together
The Readers and Writers Problem
P(mutex);
rc = rc+1;
if(rc==1) P(db);
V(mutex);
<read data>
while(1) {
P(mutex);
<do things>
rc = rc-1;
if(you_want)
if(rc==0) V(db);
READ;
V(mutex);
else
WRITE;
} P(db);
<write data>
V(db);

One solution to the readers and writers problem


But too many readers can starve writers

The Readers and Writers Problem


P(stopreaders);
P(mutex);
rc = rc+1;
if(rc==1) P(db);
V(mutex);
V(stopreaders);
while(1) { <read data>
<do things> P(mutex);
if(you_want) rc = rc-1;
READ; if(rc==0) V(db);
else V(mutex);
WRITE;
P(stopreaders);
}
P(db);
V(stopreaders);
<write data>
V(db);

Another solution to the readers and writers problem


Other wait-and-signal mechanisms

Event Count (Reed 1977)

• Init( ec )
– Set the eventcount to 0
• Read( ec )
– Return the value of eventcount ec
• Advance( ec )
– Atomically increment ec by 1
• Await( ec, v )
– Wait until ec >= v
Bounded PCP with Event Count
out
in=out=0;
B

producer() { consumer() { in
int next = 0; int next = 0;
Capacity: N

while (1) { while ( 1 ) {


produce an item next++;
next++; await(in, next);
await(out, next - N); take an item from buffer;
put the item in buffer; advance(out);
advance(in); consume the item;
} }
} }
• Does this work for more than one producer and
consumer?
• No, we will get multiple events happening, need a
sequencer

Condition Variables

• Wait (cond, mutex) • Signal (cond)


– Insert(caller, – Unblock first in
cond_queue) cond_queue, or just
– V(mutex) return if empty
– Block this thread
– When unblocked,
P(mutex)

No counting, unused signals are ignored


Insert, Unlock and Block not interrupted
Unbounded PCP using Condition Variable
Q

GET (buf):
PUT (msg):

Producer Consumer

while(1) { while(1) {
<process> P(mutex);
P(mutex); while(empty) {
<insert> wait(cond,mutex);
signal(cond); }
V(mutex); <remove>
} V(mutex);
No problems
<process>
}

Unbounded PCP using Condition Variable


Q

GET (buf):
PUT (msg): while(1) {
Consumer
P(mutex);
Producer
while(empty) {
wait(cond,mutex);
}
while(1) { <remove>
<process> V(mutex);
P(mutex); <process>
<insert> }
signal(cond);
V(mutex); while(1) {
} P(mutex);
while(empty) {
wait(cond,mutex);
}
<remove>
No problems either V(mutex);
<process>
}
Emulations

• Not all wait-and-signal mechanisms exist in all


operating systems or thread packages

• Windows has no native condition variables


– But semaphores (and mutexes)
• Some Unix-like systems have no native semaphores
– But condition variables and mutexes

• Emulations

Building Condition Variables using Semaphores


cond:
semaphore lock = 1
semaphore signal = 0
int waiters = 0

wait(cond,mutex) { signal(cond) {
P(cond.lock); P(cond.lock);
cond.waiters+=1; if(cond.waiters>0) {
V(cond.lock); V(cond.signal);
V(mutex); But no lost signal V(cond.unlock);
P(cond.signal); because of } else {
P(cond.lock); cond.waiters & V(cond.unlock);
cond.waiters-=1; counting in }
V(cond.lock); semaphores }
P(mutex);
}
Looks like lost-signal situation in
signal-and-wait
Condition Variables Extension

• Wait (cond, mutex) • Signal (cond)


– Insert(caller, – Unblock first in
cond_queue) cond_queue, or just
– V(mutex) return if empty
– Block this thread
– When unblocked, • Broadcast (cond)
P(mutex) – Unblock all in
cond_queue, or just
return if empty

Building Condition Variables using Semaphores


cond:
semaphore lock = 1
semaphore signal = 0
int waiters = 0

wait(cond,mutex) { broadcast(cond) {
P(cond.lock); P(cond.lock);
cond.waiters+=1; if(cond.waiters>0) {
V(cond.lock); for(i=0;i<cond.waiters;i++)
V(mutex); V(cond.signal);
P(cond.signal); V(cond.unlock);
P(cond.lock); } else {
cond.waiters-=1; V(cond.unlock);
V(cond.lock); }
P(mutex); }
}
Condition Variables Extension II

• Wait (cond, mutex) • Signal (cond)


– Insert(caller, – Unblock first in
cond_queue) cond_queue, or just
– V(mutex) return if empty
– Block this thread
– When unblocked, • Broadcast (cond)
P(mutex) – Unblock all in
cond_queue, or just
• Wait(cond,mutex,timeout) return if empty
– Wait no longer than
timeout

This needs additional scheduler support

Building Semaphores using Condition Variables


and Mutexes
semaphore:
mutex mutex
cond cond
int val = <initial sempahore value>

V(sem) { P(sem) {
acquire(sem.mutex); acquire(sem.mutex);
sem.val += 1; sem.val -= 1;
if(sem.val <= 0) if(sem.val < 0)
signal(sem.cond); wait(sem.cond,sem.mutex);
release(sem.mutex); release(sem.mutex);
} }
Barriers

• Use of a barrier
– threads approaching a barrier
– all threads but one blocked at barrier
– last thread arrives, all are let through

Threads wait for …

• Access to a critical region


– Mutex
– Semaphore
• A condition to be fulfilled
– Condition variable
– Barrier
– Semaphore

You might also like