You are on page 1of 41

Process Synchronization

Semaphores for
Process Synchronization
Using Semaphores for process synchronization:
A semaphore can be used to provide a mechanism
for synchronizing concurrent processes.

Example:
Suppose that two concurrent processes P1 and P2
need to be synchronized
y as follows:
 If P1 has statement S1 and P2 has statement S2 and it is
required that S2 be executed only after S1 has completed
 Then let semaphore synch be initialized to zero and shared
by the two processes:

1
Example
Process Synchronization
Then the two processes will look like:

P1:
S1;
synch.signal();

P2:
synch.wait();
S2;

Classical
Problems of Synchronization
 Bounded-Buffer Problem
 Readers and Writers Problem
 Dining-Philosophers Problem

2
Bounded-Buffer Problem
1. The Producer
Producer-Consumer
Consumer Problem:
 For Inter-Process Communication, a process writes the
message in a shared area, it is called the producer.

 The other process can then read the message from the
shared area, and it is called the consumer.

 The two processes should be synchronized and


semaphores can be used to do that.

 In general a producer process exists for the purpose of


producing data continuously, and a consumer process
exists for the purpose of consuming this data.

Solution to
Producer-Consumer Problem
Suppose that the shared area is structured as
a circularly linked list of N buffers, each can
hold one item as shown:

p c

3
Solution to
Producer-Consumer Problem
Pointers:
p points at the next empty buffer.
c points at the next buffer to consume.

Semaphores:
 Use a binary semaphore mutex to protect the buffer pool.
 Use two counting semaphores, empty and full.

Initialize:
mutex = 1
empty = N
full = 0

Solution to
Producer-Consumer Problem
Processes:
Producer: Consumer:
while (true) while (true)
{ {
// Produce an item full.wait();
empty.wait(); mutex.wait();
mutex wait();
mutex.wait(); // Empty buffer at c
// Fill buffer at p // Advance pointer c
// Advance pointer p mutex.signal();
mutex.signal(); empty.signal();
full.signal(); // Consume the removed item
} }
8

4
Readers-Writers Problem
2. The Readers-Writers
Readers Writers Problem:
 When a database is shared among several
concurrent threads, some of these threads may
want only to read the database, while others
may want to update or change the database.

 Readers:
Onlyy read the database;; theyy do not p
perform anyy
updates

 Writers:
Can both read and write.

Readers-Writers Problem
 A writer thread should have mutual
exclusion while in CS.

 Reader threads should be allowed in


their CS without mutual exclusion
between them.

 Assume unlimited number of readers


are allowed to read at the same time.
10

5
Solution 1 to
Readers-Writers Problem
Case 1: Minimum Delayy for Readers
(Strong Reader Preference)

Semaphores: Binary mutex, and Binary db.

Variables: Shared variable reader_count for readers.

Initialize:
mutex = 1;
db = 1;
reader_count = 0;

11

Solution 1 to
Readers-Writers Problem
Processes:
Reader: acquireReadLock Writer: acquireWriteLock
while (true) { while (true) {
mutex.wait(); db.wait();
reader_count++; write shared data
if (reader_count == 1) db.wait(); db.signal();
mutex.signal(); }
Read shared data releaseWriteLock
mutex.wait();
reader_count--;
if (reader_count == 0) db.signal(); releaseReadLock
mutex.signal();
} Called: read-write locks
12

6
Solution 2 to
Readers-Writers Problem
Case 2: Writers have p
priorities over Readers
(Strong writer preference).

Semaphores:Binary mutex1, mutex2, mutex3,


Binary db and rd.

Variables: Shared variable reader_count for readers,


Shared variable writer_count for writers.

Initialize:
mutex1, mutex2, mutex3, db, and rd = 1.
reader_count = 0.
writer_count = 0.

13

Solution 2 to
Readers-Writers Problem
Processes:
Reader: Writer:
while (trure) { while (true) {
mutex3.wait(); mutex2.wait();
rd.wait(); writer_count++;
mutex1.wait(); if (writer_count == 1) rd.wait();
reader_count++; mutex2.signal();
if (reader_count == 1) db.wait(); db.wait();
mutex1.signal(); write shared data
rd.signal(); db.signal();
mutex3.signal(); mutex2.wait();
Read shared data writer_count--;
mutex1.wait(); if (writer_count == 0) rd.signal();
reader_count--; mutex2.signal();
if (reader_count == 0) db.signal(); }
mutex1.signal();
} 14

7
Solution 2 to
Readers-Writers Problem
Notes:
 If there is a writer in CS, then all new
readers except one will wait at mutex3,
and exactly one reader will wait at rd.

 All new writers will wait at db.

 Only when last writer leaves its CS a


reader can enter its CS.

15

Dining-Philosophers Problem
3. Dining-Philosophers
Dining Philosophers Problem:
 N philosophers spend their lives thinking and eating.

 They share a common circular table surrounded by N


chairs, each belonging to one philosopher.

 In the center of the table there is a bowl of rice, and


the table is laid with N single chopsticks.

 If a philosopher is not eating, he is thinking, and if he


feels hungry he must pick up the two chopsticks that
are closest to him one at a time. He can only eat if he
has two chopsticks.

16

8
Dining-Philosophers Problem

17

Dining-Philosophers Problem
 After eating he must put down his
chopsticks and start thinking again.
 How can we model the behavior of each
philosopher such that we provide
maximum overlap of eating philosophers
and
d avoid:
id
 Deadlock
 Indefinite waiting ?

18

9
Solution to
Dining-Philosophers Problem
Solution: (first try)
Represent each chopstick by a binary
semaphore:

semaphore chopstick[5];

Initialize:
All elements of chopstick to 1.

19

Solution to
Dining-Philosophers Problem
Processes:
Each philosopher i has the following structure:

while (true)
{
chopstick[ i ].wait();
chopstick[ (i + 1) % N ].wait();
Eat
chopstick[ i ].signal();
] signal();
chopstick[ (i + 1) % N ].signal();
Think
}

Problem: A deadlock is possible.


20

10
Another Synchronization Tool
Event Counts
Event Counts:
 An event count keeps track of the number of
occurrences of a class of related events.

 It is an integer counter that does not decrease.

 It automatically
t ti ll initializes
i iti li to
t zero when
h it is
i created.
t d

21

Event Count Operations


 There are only 3 primitive operations on
any event count, E:
1. E.read():
Returns the current value of event count E.

2. E.advance():
Atomicallyy increments event count E byy one.

3. E.await(v):
Wait (block) the process executing this command
until event count E has the value of v or more.

22

11
Event Counts for
Process Synchronization
Process synchronization can be achieved without
using mutual exclusion.

Example:
 Solving the producer-consumer problem, using a circular
array of buffers of size n = 5.

 B ffe s a
Buffers are
ennumbered
mbe ed 0 … n-1.
n1

 Use two event counts:


In, counts number of items producer put in the buffer.
Out, counts number of items consumer removed.

23

Solution to
Producer-Consumer Problem
Processes:
ocesses
Producer: Consumer:
i = 0; // local variable i = 0; // local variable
while (true) while (true)
{ {
i++;; i++;;
out.await(i-n); in.await(i);
fill buffer at i%n consume buffer at i%n;
in.advance(); out.advance();
} }
24

12
Concurrent Programming

Problems with Semaphores


 The semaphores and other techniques
discussed previously are considered
low level programming techniques to
provide mutual exclusion or process
synchronization

 A single programming error may


cause serious problems:

26

13
Example Synchronization Errors
Consider semaphore used for mutual exclusion:

mutex.wait();
CS;
mutex.signal();

1. If we reversed the above to become:

mutex.signal();
mutex signal();
CS;
mutex.wait();

Then several processes may execute in their CS at the


same time.
27

Example Synchronization Errors


2. If by mistake a wait() is placed instead of signal():

mutex.wait();
CS;
mutex.wait();

Then a deadlock will occur.

3
3. If by mistake an omission of one or both occurred:

Omitting mutex.wait() will cause no mutual exclusion.


Omitting mutex.signal() will cause deadlock.

28

14
Concurrent Programming
 Modern Operating Systems are written using high level
l
languages rather
th than
th assembly
bl

 These languages provide high-level language constructs to


provide mutual exclusion and process synchronization in
general

 Such languages are called “Concurrent Languages”, and


writing concurrent programs using these languages is known
as “Concurrent
Concurrent Programming”
Programming

 Examples of concurrent languages include: Concurrent Pascal,


Mesa, C# and Java

29

Empty Slide

٣٠

15
Monitors
Monitors:
A monitor is a high-level abstraction that provides a convenient
and effective mechanism for process synchronization
 Provides one lock and zero or more condition variables for
managing concurrent access to shared data
 It also allows the safe and effective sharing of objects among
several processes
 Only one process may be active within the monitor at a time
 Defining a monitor is like defining a class with the keyword
Monitor instead.
 An instance, creates a monitor object with mutual exclusion.

٣١

Structure &
Schematic view of a Monitor

monitor monitor-name
{
// shared variable declarations

function F1 (…) { …. }

f nction Fn ((…)) {……}


function { }

initialization_code ( ….) { … }

}

This provides only Mutual Exclusion


32

16
Condition Variables
 For the general synchronization problems the monitor could
i l d one or more variables
include i bl off type
t condition
diti
 A condition variable is a queue and two operations only:
wait and signal
 Example:
condition x, y;
 x.wait(): A process that invokes this operation loses the
monitor’s lock and is suspended and placed in
th queue off condition
the diti variable
i bl x until
til another
th
process calls x.signal(). Monitor’s lock is released

 x.signal(): Resumes exactly one of the processes (if any)


that invoked x.wait(). Now two processes are
inside the monitor !!!! Which one is allowed?

٣٣

Monitor with Condition Variables

34

17
Types of Monitors
 Monitors differ in the way they handle the situation when a
th d waiting
thread iti ini a condition
diti queue is
i released
l d by
b another
th
thread executing a signal within the monitor. Four choices:
1. Signal & Continue (SC) -- Mesa-style (Java and most OSs):
The signaling thread keeps the lock and the CPU until it exits;
while the signaled thread will be awaken but need to acquire
the lock again before continuing.
Not safe: because a signaling thread may make the condition invalid, or
it may signal yet another condition before the signaled thread continues.
Practically, the signaled thread need to check condition again after wait.

2. Signal & Wait (SW ):


The signaling thread is blocked (losing the lock & CPU), it must
wait again for the lock to continue; while the signaled thread is
directly awaken; given the lock & CPU to continue its operations
٣٥

Types of Monitors
3. Signal
g & Urgent
g Wait (SU)
( ) -- Hoare-style
y ((Most textbooks):
)
Like SW but the signaling thread has the guarantee that it
would continue just after the signaled thread is done.

4. Signal & Exit (SX):


The signaling thread exits from the monitor directly after the
signal; while the signaled thread can start directly.
This method is not often used.

 Note:
Thus, the process executing a signal operation:
1. Must wait until the resumed process either leaves the
monitor or waits for another condition; or
2. Make the signal as its last statement just before exiting
the monitor.

٣٦

18
Solution to Dining Philosophers
monitor
it DP
{
enum { THINKING; HUNGRY, EATING } state [5] ;
condition self [5];

void pickup (int i) {


state[i] = HUNGRY;
test(i);
if (state[i] != EATING) self[i].wait();
}

void putdown (int i) {


state[i] = THINKING;

// test left and right neighbors


test((i + 4) % 5);
test((i + 1) % 5);
}

37

Solution to Dining Philosophers


void
id ttestt (int
(i t i) {
if ( (state[(i + 4) % 5] != EATING) && // Left neighbor is not eating
(state[i] == HUNGRY) && // I am hungry
(state[(i + 1) % 5] != EATING) ) { // Right neighbor is not eating
state[i] = EATING ; // Change my state to eating
self[i].signal() ;
}
}

initialization_code()
initialization code() {
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}
}

38

19
Solution to Dining Philosophers

 Each philosopher i invokes the operations pickup() and


putdown() in the following sequence:

Dp.pickup(i);

EAT

Dp.putdown(i);
p putdo ( );

 Starvation is possible

39

Proof of Equivalence

 To show that one concurrency construct


(e.g. Semaphores) is equivalent to another
(e.g. Monitors) we need to:

Build each using the other.

40

20
Monitor Implementation
A Monitor may be implemented using Semaphores as follows:

 Variables:
semaphore mutex; // initialize to 1, used for monitor entry
semaphore next; // initialize to 0, used for signaling processes
int next_count; // initialize to 0, as No. of waiting processes

 To ensure mutual exclusion within the monitor, each function defined in the
interface part of the monitor can be written as follows, assume function name is F:

mutex.wait();
… … …
body of F;
… … …
if (next_count > 0)
next.signal();
else
mutex.signal();

41

Monitor Implementation
Condition Variable implementation:
For each condition variable x we define the
following:

 Variables:
semaphore x_sem;
x sem; // initialized to 0
int x_count; // initialized to 0

42

21
Monitor Implementation
Operations:
x.wait(): x.signal():
x_count++; If (x_count > 0)
if (next_count > 0) {
next.signal(); next_count++;
else x_sem.signal();
mutex.signal();
t i l() next.wait();
t it()
x_sem.wait(); next_count--;
x_count--; }
Function x.hasWaiters(): // Optional method,
{ return (x_count>0)} // true if there are processes waiting for x.signal()

43

Semaphore Implementation
A Semaphore
S h may be
b implemented
i l d using
i a Monitor
M i as follows:
f ll

monitor Semaphore {
initialize(count) { // initialization op.
int v = count; // initial value
condition queue; // condition variable
}
sem_wait(); // semaphore wait op.
sem_signal(); // semaphore signal op.
}
44

22
Semaphore Implementation
Operations:
sem_wait() sem_signal()
{ {

v = v - 1; v = v + 1;
if (v < 0) if (v < 1)
q.wait // condition variable q.signal // condition variable

} }

This proves that both Monitors and Semaphores are equivalent


synchronization tools; However, monitors are more structured.
45

Empty Slide

٤٦

23
Using a Monitor to Solve
Readers-Writers Problem
Example:
A monitor
i for
f NoN preference
f b
between readers
d and
d writers:
i

Monitor RWM
{
int reader_count = 0;
bool some_one_writing = false;
condition read, write;

void start_reading()
{
if (some_one_writing || write.hasWaiters()) read.wait();
reader_count++;
read.signal();
}

٤٧

Using a Monitor to Solve


Readers-Writers Problem
void stop_reading()
{
reader_count--;
if (reader_count == 0) write.signal();
}

void start_writing()
{
if (reader_count > 0) || (some_one_writing) write.wait();
some_one_writing = true;
}

void stop_writing()
{
some_one_writing = false;
if (read.hasWaiters()) read.signal();
else write.signal();
}
}

٤٨

24
Using a Monitor to Solve
Readers-Writers Problem
Then each reader or writer process/thread will run the following code:

Reader:
RWM.start_reading();
// Read shared data
RWM.stop_reading();

Writer:
RWM.start_writing();
// Write shared data
RWM.stop_writing();

٤٩

Empty Slide

٥٠

25
Java Monitors and Threads
 Java pprovides a Monitor-like concurrencyy mechanism for thread synchronization
y
 Every Object in Java has associated with it a single Lock
 When a method is declared synchronized, calling it requires owning its Object’s lock
 If the lock is not available, the caller is blocked and placed in the Object’s entry set
 Example:
public class SimpleClass {
......
public synchronized void safeMethod () {
......
/* implementation of safeMethod() */
}
}
If sc is instance of SimpleClass, calling sc.safeMethod() will Mutex sc or blocks
 To simplify its use, Java provides only one implicit condition variable with only one
waiting set and wait() & notify() methods that work similar to wait() & signal().

٥١

Java Monitors and Threads

٥٢

26
Using a Java Monitor to Solve
Readers-Writers Problem
Example:
A Java
J monitor
i for
f the
h readers
d and
d writers
i synchronization
h i i problem: bl
This solution does not guarantee fairness, and it may lead to starvation,
because the waiting sets of the lock and the implicit condition variable
are NOT FIFO queues!

import java.util.concurrent.locks.*;

public class RWMonitor {


private int readcount;
private boolean some_one_writing;

public RWMonitor() {
readcount = 0;
some_one_writing = false;
}

٥٣

Using a Java Monitor to Solve


Readers-Writers Problem
// First reader acquires the monitor lock. Must use the while-try constructs
public
bli synchronized
h i d void id start_reading()
t t di () {
while (some_one_writing) {
try {
this.wait();
} catch (InterruptedException e) { }
}
readcount++;
}

// Last reader releases the monitor lock, to allow waiting writers in


public synchronized void stop_reading() {
readcount--;
if (readcount == 0) this.notify();
}

٥٤

27
Using a Java Monitor to Solve
Readers-Writers Problem
// A writer acquires the monitor lock. Must use the while-try constructs
public
bli synchronized
h i d void id start_writing()
t t iti () {
while ((readcount > 0) || (some_one_writing)) {
try {
this.wait();
} catch (InterruptedException e) { }
}
some_one_writing = true;
}

// A writer releases the monitor lock,, to allow waiting


g threads in
public synchronized void stop_writing() {
some_one_writing = false;
this.notifyAll();
}
} // the end of the RWMonitor class

٥٥

Using a Java Monitor to Solve


Readers-Writers Problem
public static void main ((String
p g argv[])
g []) {
RWMonitor m = new RWMonitor();
Reader R = new Reader(); // holds an instance of class Reader
Writer W = new Writer(); // holds an instance of class Writer
W.start(); R.start(); // Start executing the reader and writer threads
}
The Reader: The Writer:
public class Reader extends Thread { public class Writer extends Thread {
public Reader() { . . . . } public Writer() { . . . . }
public void run () {
p public void run () {
p
m.start_reading(); m.start_writing();
//. . . READ . . . // //. . . WRITE . . . //
m.stop_reading(); m.stop_writing();
} }
} }

٥٦

28
A more flexible approach to Solve
Readers-Writers Problem in Java
We can explicitly use Java locks and condition variables
i
instead
d off the
h synchronized
h i d keyword
k d to have
h a more flexible
fl ibl
monitor implementation as in the following example:

Example:
This solution guarantees fairness, but it may lead to
starvation still.

import java.util.concurrent.locks.*;

public class RWFairMonitor {


private ReentrantLock lock; // an explicit reentrant recursive lock
private Condition read, write; // two explicit condition variables
private int readcount;
private boolean some_one_writing;

٥٧

A more flexible approach to Solve


Readers-Writers Problem in Java
public RWFairMonitor() { // the monitor constructor
l k = new ReentrantLock(true);
lock R t tL k(t ) // ttrue ffor a FIFO queue
read = lock.newCondition(); // associate the read cond. with lock
write = lock.newCondition(); // associate the write cond. with lock
readcount = 0;
some_one_writing = false;
}

٥٨

29
A more flexible approach to Solve
Readers-Writers Problem in Java
// First reader tries to acquire the monitor lock.
// Must
M t use the th try-finally
t fi ll construct.
t t
public void start_reading() {
lock.lock(); // acquire the entry lock; defined FIFO.
try {
while (some_one_writing) { // check if you should wait – while is advised
read.awaitUninterruptibly(); // wait until signaled. Non-interruptible
}
readcount++; // increment the counter
} finally {
l k.unlock();
lock l k() // release the monitor’s lock
}
}

٥٩

A more flexible approach to Solve


Readers-Writers Problem in Java
// Last reader releases the monitor lock, to allow waiting writers in.
// Must
M t use the th try-finally
t fi ll construct.
t t
public void stop_reading() {
lock.lock(); // acquire the entry lock; defined FIFO.
try {
readcount--; // decrement the counter
if (readcount == 0) { // if last reader, then
write.signalAll(); // signal all waiting writers
read.signalAll(); // and readers
}
} finally
fi ll {
lock.unlock(); // release the monitor’s lock
}
}

٦٠

30
A more flexible approach to Solve
Readers-Writers Problem in Java
// A writer acquires the monitor lock.
// Must
M t use the th try-finally
t fi ll construct.
t t
public void start_writing() {
lock.lock(); // acquire the entry lock; defined FIFO.
try {
while ((readcount > 0) || (some_one_writing)) { // check if should wait
write.awaitUninterruptibly(); // wait until signaled. Non-interruptible
}
some_one_writing = true;
} finally {
l k.unlock();
lock l k() // release the monitor’s lock
}
}

٦١

A more flexible approach to Solve


Readers-Writers Problem in Java
// A writer releases the monitor lock, to allow waiting threads in
// Must
M t use the th try-finally
t fi ll construct.
t t
public void stop_writing() {
lock.lock(); // acquire the entry lock; defined FIFO.
try {
some_one_writing = false;
read.signalAll(); // signal all waiting readers
write.signalAll(); // and writers
} finally {
lock.unlock(); // release the monitor’s lock
}
}
} // the end of the RWFairMonitor class

٦٢

31
A more flexible approach to Solve
Readers-Writers Problem in Java
public static void main ((String
p g argv[])
g []) {
RWFairMonitor m = new RWFairMonitor();
Reader R = new Reader(); // holds an instance of class Reader
Writer W = new Writer(); // holds an instance of class Writer
W.start(); R.start(); // Start executing the reader and writer threads
}
The Reader: The Writer:
public class Reader extends Thread { public class Writer extends Thread {
public Reader() { . . . . } public Writer() { . . . . }
public void run () {
p public void run () {
p
m.start_reading(); m.start_writing();
//. . . READ . . . // //. . . WRITE . . . //
m.stop_reading(); m.stop_writing();
} }
} }

٦٣

Problems with Monitors


 Nested monitors
monitors, if allowed may
result in deadlock.
 Monitors are implemented in few
concurrent languages.
 They don
don’tt provide information
exchange between machines.

٦٤

32
Example Synchronization Tools
Used in Operating Systems
 Solaris
 Windows
 Linux
 Pthreads

٦٥

Solaris Synchronization
 Implements
p a varietyy of locks to support
pp multitasking,
g,
multithreading (including real-time threads), and
multiprocessing
 Uses adaptive mutexes (spinlocks / sleep locks) for
efficiency when protecting data from short code
segments
 Uses condition variables and read-write locks when
l
longer sections
ti off code
d need
d access to
t ddata
t
 Uses turnstiles (a queue of threads blocked on a
single lock) to order the list of threads waiting to
acquire either an adaptive mutex or read-write lock

٦٦

33
Windows Synchronization
 Uses interrupt
p masks to p
protect access to g
global resources on
uni-processor systems

 Uses spinlocks on multi-processor systems

 Also provides dispatcher objects which allow threads to


synchronize according to several mechanisms, including,
mutexes, semaphores, events and timers

 Dispatcher
p objects
j mayy provide
p events
 An event acts much like a condition variable

 Dispatcher objects may be in either of two states:


 Signaled state: Object is available, acquisition causes no blocking
 Non-signaled state: Object is not available, causes blocking if
acquisition is attempted

٦٧

Linux Synchronization
 Linux provides:
 Semaphores
 atomic integers
 Spinlocks
 Read-write locks
 For long critical sections it uses semaphores and their
read-write lock versions
 For short critical sections:
 On single processor systems, it disables/enables preemption
 On SMP systems, it uses spinlocks and their read-write lock
versions

٦٨

34
Linux Synchronization (cont.)
Atomic variables
provide atomic (uninterruptible) updates on basic data types
such as integers and booleans
 The declared type for atomic integer is defined as: atomic_t
 Example: Consider the variables:
atomic_t counter;
int value;

٦٩

POSIX Synchronization
 Pthreads API is OS
OS-independent
independent

 It provides:
 Mutex locks
 Condition variables
 Read-write locks

 Non-portable extensions include:


 Spinlocks
 Semaphores

٧٠

35
POSIX Mutex Locks
 Creating and initializing the lock:
#include <pthread.h>

pthread_mutex_t mutex;
pthread_mutex_init (&mutex, NULL); //create and initialize the mutex lock

 Acquiring and releasing the lock:


pthread_mutex_lock
th d t l k (&mutex);
(& t ) // acquire
i ththe mutex
t lock
l k

/* … critical section … */

pthread_mutex_unlock (&mutex); // release the mutex lock

٧١

POSIX Semaphores
 POSIX provides two versions:
 named
 can be used by unrelated processes

 unnamed
 cannot be used by unrelated processes

٧٢

36
POSIX Named Semaphores
 Creating and initializing the semaphore:
#include <semaphore.h>
sem_t *sem;
/* Create the semaphore and initialize it to 1 */
sem = sem_open ("SEM", O_CREAT, 0666, 1);

 Another process can access the semaphore by referring to its


name SEM, acquiring and releasing the semaphore:
sem_wait (sem); //acquire the semaphore
/* … critical section … */
sem_post (sem); //release the semaphore

٧٣

POSIX Unnamed Semaphores


 Creating and initializing the semaphore:
#include <semaphore.h>
sem_t sem;
/* Create the semaphore and initialize it to 1 */
sem = sem_init (&sem, 0, 1);

 Acquiring
q g and releasing
g the semaphore:
p
sem_wait (&sem); //acquire the semaphore
/* … critical section … */
sem_post (&sem); //release the semaphore

٧٤

37
POSIX Condition Variables
 Since POSIX is typically used in C/C++ and these languages do
not provide a monitor, POSIX condition variables are associated
with a POSIX mutex lock to provide mutual exclusion

 Creating and initializing the condition variable:

pthread_mutex_t mutex;
pthread cond t cond_var;
pthread_cond_t cond var;
pthread_mutex_init (&mutex, NULL);
pthread_cond_init (&cond_var, NULL);

٧٥

POSIX Condition Variables (cont.)


 Thread waiting for the condition a == b to become true:
pthread_mutex_lock (&mutex); // acquire the lock
while (a != b)
/* while waiting, associate the lock with the condition variable */
pthread_cond_wait (&cond_var, &mutex);
pthread_mutex_unlock (&mutex); // release the lock

 Thread signaling another thread waiting on the condition


variable:
i bl
pthread_mutex_lock (&mutex); // acquire the lock
a = b; // making the condition true
pthread_cond_signal (&cond_var); // signal the condition variable
pthread_mutex_unlock (&mutex); // release the lock

٧٦

38
Liveness
 Processes may have to wait indefinitely while trying
to acquire a synchronization tool such as a mutex
lock or semaphore
 Waiting indefinitely violates the progress and
bounded-waiting criteria discussed at the beginning
of this chapter
 Liveness refers to a set of properties that a system
must satisfy to ensure processes make progress
 Indefinite waiting is an example of a liveness failure.

٧٧

Liveness (cont.)
Deadlock
Two or more processes are waiting indefinitely for an event that
can be caused by only one of the waiting processes
 Example: Let S and Q be two semaphores initialized to 1
P0 P1
wait(S); wait(Q);
wait(Q); wait(S);
… …
signal(S); signal(Q);
signal(Q); signal(S);

 Consider if P0 executes wait(S) and P1 wait(Q). When P0 executes


wait(Q), it must wait until P1 executes signal(Q)
 However, P1 is waiting until P0 execute signal(S).
 Since these signal() operations will never be executed, P0 and P1 are
deadlocked.
٧٨

39
Liveness (cont.)
Other forms of deadlock:
 Starvation – indefinite blocking
A process may never be removed from the semaphore queue in
which it is suspended

 Priority Inversion – A Scheduling problem


Wh lower-priority
When l i it process holds
h ld a lock
l k needed
d d by
b higher-
hi h
priority process
 Solved via priority-inheritance protocol

٧٩

Priority Inheritance Protocol


 Example:
a pe
Consider the scenario with three processes: P1, P2, and P3.
P1 has the highest priority, P2 the next highest, and P3 the
lowest. Assume P3 is assigned a resource R that P1 wants. Thus,
P1 must wait for P3 to finish using the resource. However, P2
becomes runnable and preempts P3.
 What has happened is that P2 – with lower priority than P1 –
has indirectly prevented P1 from gaining access to the resource.
 To solve this problem, a priority inheritance protocol is used:
 This simply allows the priority of the highest thread waiting to access
a shared resource to be assigned to the thread currently using the
resource. Thus, the current owner of the resource is assigned the
priority of the highest priority thread wishing to acquire the resource.

٨٠

40
Priority Inversion and
the Mars Pathfinder
 Priority inversion can be more than a scheduling inconvenience. On systems with tight time
constraints—such as real-time systems—priority inversion can cause a process to take
longer than it should to accomplish a task. When that happens, other failures can cascade,
resulting in system failure.
 Real Example: The Mars Pathfinder, a NASA space probe that landed a robot, the Sojourner
rover, on Mars in 1997 to conduct experiments. Shortly after the Sojourner began
operating, it started to experience frequent computer resets. Each reset reinitialized all
hardware and software, including communications. If the problem had not been solved, the
Sojourner would have failed in its mission.
 The Problem: One high-priority task, called “bc dist,” was taking longer than expected to
complete its work
work. This task was being forced to wait for a shared resource that was held
by the lower-priority task called “ASI/MET”, which was preempted by multiple medium-
priority tasks. The “bc dist” task would stall waiting for the shared resource, and ultimately
the “bc sched” task would discover the problem and perform the reset. The Sojourner was
suffering from a typical case of priority inversion.
 The Solution: The operating system on the Sojourner was the VxWorks – a real-time OS,
it had a global variable to enable priority inheritance on all semaphores. After testing,
the variable was set on the Sojourner (on Mars!), and the problem was solved.

٨١

41

You might also like