Lecture 5 - Process Synchronization
Lecture 5 - Process Synchronization
Chapter five
Process Synchronization
(Materials partly taken from Operating System Concepts by Silberschatz, Galvin and Gagne, 2005 – 7 th Edition, chapter 1-2)
3/30/23 1
3/30/23 4
Operating Systems
Producer
while (true) {
/* produce an item in next_produced */
while (true) {
while (counter == 0) ; /* do nothing */
next_consumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
counter--;
/* consume the item in next consumed */
}
Race condition
3/30/23 7
Operating Systems
Race Condition
register1 = counter
register1 = register1 + 1
counter = register1
• counter-- could be implemented as
register2 = counter
register2 = register2 - 1
counter = register2
• Consider this execution interleaving with “count = 5” initially:
S0: producer execute register1 = counter {register1 = 5}
S1: producer execute register1 = register1 + 1 {register1 = 6}
S2: consumer execute register2 = counter {register2 = 5}
S3: consumer execute register2 = register2 – 1 {register2 = 4}
S4: producer execute counter = register1 {counter = 6 }
S5: consumer execute counter = register2 {counter = 4}
Critical Section problem
• Inconsistency in the state of data, files or tables being accessed by the
multiple process at the same time can be attributed to a specific
section of code which we call as critical section.
• Critical section is that part of code executing which in concurrent
fashion, two process may come in the state of confusion or
inconsistency.
• Critical section problem is how to allow to enter and to execute
critical section code to different cooperating processes, so that they
are able to execute this code in synchronized fashion, as well as they
maintain the consistency
3/30/23 9
Operating Systems
Solution to critical section problem
• In general only one process should be allowed to execute the critical
section code.
• Operating system shall enforce a protocol that every participating
process should follow in order to enter and exit from the critical
section.
• A simple solution to manage the entry of single process in critical
segment of code is to manage a variable turn which takes two values,
one if a process has its turn to execute the critical section code
otherwise zero.
3/30/23 10
Operating Systems
Critical Section
do {
turn = j;
remainder section
} while (true);
Properties of a valid Solution to Critical-Section Problem
3/30/23 16
Operating Systems
Algorithm for Process Pi
do {
flag[i] = true;
turn = j;
while (flag[j] && turn = = j);
critical section
flag[i] = false;
remainder section
} while (true);
Cross check the guarantees
As a result of this, operating systems using this technique is not broadly scalable.
Atomic instructions
• Modern machines provide special atomic hardware instructions
• Atomic = non-interruptible
These atomic instructions either test and set the value of a memory
word, or they can swap the content of two memory locations without
being interrupted.
3/30/23 20
Operating Systems
Solution to Critical-section Problem Using Locks
do {
acquire lock
critical section
release lock
remainder section
} while (TRUE);
test_and_set Instruction
Definition:
boolean test_and_set (boolean *target)
{
boolean rv = *target;
*target = TRUE;
return rv:
}
1. Executed atomically
2. Returns the original value of passed parameter
3. Set the new value of passed parameter to “TRUE”.
Solution to critical section problem using test and set
• The process which executes the test and set instruction will hold the
lock by initializing a Boolean lock variable to false.
3/30/23 23
Operating Systems
Solution using test_and_set()
} while (true);
compare_and_swap Instruction
Definition:
int compare _and_swap(int *value, int expected, int new_value) {
int temp = *value;
if (*value == expected)
*value = new_value;
return temp;
}
1. Executed atomically
2. Returns the original value of passed parameter “value”
3. Set the variable “value” the value of the passed parameter
“new_value” but only if “value” ==“expected”. That is, the
swap takes place only under this condition.
Solution to critical section problem using compare and swap
3/30/23 26
Operating Systems
Solution using compare_and_swap
do {
waiting[i] = true;
key = true;
while (waiting[i] && key)
key = test_and_set(&lock);
waiting[i] = false;
/* critical section */
j = (i + 1) % n;
while ((j != i) && !waiting[j])
j = (j + 1) % n;
if (j == i)
lock = false;
else
waiting[j] = false;
/* remainder section */
} while (true);
Mutex Locks
Operating Systems
Semaphore
• Definition of the wait() operation
wait(S) {
while (S <= 0); // busy wait
S--;
}
• signal() operation
Definition of the
signal(S) {
S++;
}
CS solution using semaphore
• Semaphore integer wait and signal are atomic operations, and
if a process modifies the value of semaphore no other process
is allowed to edit the value, unless the original process is out
of CS.
• Various protocols can be designed using semaphore to
implement the solution of critical section problem for
cooperating processes.
• Operating systems generally use Binary Semaphore to
implement synchronization protocol between two processes
and a counting semaphore for implementing the
synchronization among more than two processes.
3/30/23 33
Operating Systems
Semaphore Usage
• Counting semaphore – integer value can range over an unrestricted domain
• Binary semaphore – integer value can range only between 0 and 1
• Same as a mutex lock
• Can solve various synchronization problems
• Consider P1 and P2 that require S1 to happen before S2
Create a semaphore “synch” initialized to 0
P1:
S1;
signal(synch);
P2:
wait(synch);
S2;
• Can implement a counting semaphore S as a binary semaphore
Semaphore Implementation
• Must guarantee that no two processes can execute the wait() and
signal() on the same semaphore at the same time
• Thus, the implementation becomes the critical section problem
where the wait and signal code are placed in the critical section
• Could now have busy waiting in critical section implementation
• But implementation code is short
• Little busy waiting if critical section rarely occupied
• Note that applications may spend lots of time in critical sections and
therefore this is not a good solution
Semaphore Implementation with no Busy waiting
wait(semaphore *S) {
S->value--;
if (S->value < 0) {
add this process to S->list;
block();
}
}
signal(semaphore *S) {
S->value++;
if (S->value <= 0) {
remove a process P from S->list;
wakeup(P);
}
}
Deadlock and Starvation
• Deadlock – two or more processes are waiting indefinitely for an event
that can be caused by only one of the waiting processes
• 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);
do {
...
/* produce an item in next_produced */
...
wait(empty);
wait(mutex);
...
/* add next produced to the buffer */
...
signal(mutex);
signal(full);
} while (true);
Bounded Buffer Problem (Cont.)
Do {
wait(full);
wait(mutex);
...
/* remove an item from buffer to next_consumed */
...
signal(mutex);
signal(empty);
...
/* consume the item in next consumed */
...
} while (true);
Readers-Writers Problem
• A data set is shared among a number of concurrent processes
• Readers – only read the data set; they do not perform any updates
• Writers – can both read and write
• Problem – allow multiple readers to read at the same time
• Only one single writer can access the shared data at the same time
• Several variations of how readers and writers are considered – all involve some
form of priorities
• Shared Data
• Data set
• Semaphore rw_mutex initialized to 1
• Semaphore mutex initialized to 1
• Integer read_count initialized to 0
Readers-Writers Problem (Cont.)
do {
wait(rw_mutex);
...
/* writing is performed */
...
signal(rw_mutex);
} while (true);
Readers-Writers Problem (Cont.)
• The structure of a reader process
do {
wait(mutex);
read_count++;
if (read_count == 1)
wait(rw_mutex);
signal(mutex);
...
/* reading is performed */
...
wait(mutex);
read count--;
if (read_count == 0)
signal(rw_mutex);
signal(mutex);
} while (true);
Readers-Writers Problem Variations
// eat
signal (chopstick[i] );
signal (chopstick[ (i + 1) % 5] );
// think
} while (TRUE);
• What is the problem with this algorithm?
Dining-Philosophers Problem Algorithm (Cont.)
• Deadlock handling
• Allow at most 4 philosophers to be sitting simultaneously at the
table.
• Allow a philosopher to pick up the forks only if both are
available (picking must be done in a critical section.
• Use an asymmetric solution -- an odd-numbered philosopher
picks up first the left chopstick and then the right chopstick.
Even-numbered philosopher picks up first the right chopstick and
then the left chopstick.
Problems with Semaphores
monitor monitor-name
{
// shared variable declarations
procedure P1 (…) { …. }
• condition x, y;
• Two operations are allowed on a condition variable:
• x.wait() – a process that invokes the operation is
suspended until x.signal()
• x.signal() – resumes one of processes (if any) that
invoked x.wait()
• If no x.wait() on the variable, then it has no effect on
the variable
Monitor with Condition Variables
Condition Variables Choices
• If process P invokes x.signal(), and process Q is suspended in x.wait(), what should happen
next?
• Both Q and P cannot execute in paralel. If Q is resumed, then P must wait
• Options include
• Signal and wait – P waits until Q either leaves the monitor or it waits for another
condition
• Signal and continue – Q waits until P either leaves the monitor or it waits for another
condition
• Both have pros and cons – language implementer can decide
• Monitors implemented in Concurrent Pascal compromise
• P executing signal immediately leaves the monitor, Q is resumed
• Implemented in other languages including Mesa, C#, Java
Monitor Solution to Dining Philosophers
monitor DiningPhilosophers
{
enum { THINKING; HUNGRY, EATING) state [5] ;
condition self [5];
initialization_code() {
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}
}
Solution to Dining Philosophers (Cont.)
DiningPhilosophers.pickup(i);
EAT
DiningPhilosophers.putdown(i);
• Variables
wait(mutex);
…
body of F;
…
if (next_count > 0)
signal(next)
else
signal(mutex);
x_count++;
if (next_count > 0)
signal(next);
else
signal(mutex);
wait(x_sem);
x_count--;
Monitor Implementation (Cont.)
if (x_count > 0) {
next_count++;
signal(x_sem);
wait(next);
next_count--;
}
Resuming Processes within a Monitor
R.acquire(t);
...
access the resurce;
...
R.release;
3/30/23 65
Operating Systems