Professional Documents
Culture Documents
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.
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.
5
Solution 1 to
Readers-Writers Problem
Case 1: Minimum Delayy for Readers
(Strong Reader Preference)
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).
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.
15
Dining-Philosophers Problem
3. Dining-Philosophers
Dining Philosophers Problem:
N philosophers spend their lives thinking and eating.
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
}
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 automatically
t ti ll initializes
i iti li to
t zero when
h it is
i created.
t d
21
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
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
26
13
Example Synchronization Errors
Consider semaphore used for mutual exclusion:
mutex.wait();
CS;
mutex.signal();
mutex.signal();
mutex signal();
CS;
mutex.wait();
mutex.wait();
CS;
mutex.wait();
3
3. If by mistake an omission of one or both occurred:
28
14
Concurrent Programming
Modern Operating Systems are written using high level
l
languages rather
th than
th assembly
bl
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 (…) { …. }
…
initialization_code ( ….) { … }
…
}
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
٣٣
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.
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.
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];
37
initialization_code()
initialization code() {
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}
}
38
19
Solution to Dining Philosophers
Dp.pickup(i);
EAT
Dp.putdown(i);
p putdo ( );
Starvation is possible
39
Proof of Equivalence
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
} }
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();
}
٤٧
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().
٥١
٥٢
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 RWMonitor() {
readcount = 0;
some_one_writing = false;
}
٥٣
٥٤
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;
}
٥٥
٥٦
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.*;
٥٧
٥٨
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
}
}
٥٩
٦٠
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
}
}
٦١
٦٢
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();
} }
} }
٦٣
٦٤
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
Dispatcher
p objects
j mayy provide
p events
An event acts much like a condition variable
٦٧
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
٧٠
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
/* … critical section … */
٧١
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);
٧٣
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
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);
٧٥
٧٦
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);
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
٧٩
٨٠
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