Process Synchronization
Process Synchronization
PROCESS SYNCHRONIZATION
Chapter 6: Process Synchronization
Background
The Critical-Section Problem
Peterson’s Solution
Synchronization Hardware
Mutex Locks
Semaphores
Classic Problems of Synchronization
Monitors
Background
Processes can execute concurrently
May be interrupted at any time, partially completing
execution
Concurrent access to shared data may result in data
inconsistency
Maintaining data consistency requires mechanisms to ensure
the orderly execution of cooperating processes
Illustration of the problem:
Suppose that we wanted to provide a solution to the consumer-
producer problem that fills all the buffers. We can do so by
having an integer counter that keeps track of the number of
full buffers. Initially, counter is set to 0. It is incremented by
the producer after it produces a new buffer and is decremented
by the consumer after it consumes a buffer.
Producer Consumer Problem
Race Condition
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 */
}
Critical Section Problem
Consider system of n processes {p0, p1, … pn-1}
Each process has critical section segment of code
Process may be changing common variables, updating
table, writing file, etc
When one process in critical section, no other may be in its
critical section
Critical section problem is to design protocol to solve this
Each process must ask permission to enter critical section in
entry section, may follow critical section with exit section,
then remainder section
Critical Section
do {
critical section
turn = j;
remainder section
} while (true);
Peterson’s Solution
Good algorithmic description of solving the problem
Two process solution
Assume that the load and store machine-language
instructions are atomic; that is, cannot be interrupted
The two processes share two variables:
int turn;
Boolean flag[2]
do {
flag[i] = true;
turn = j;
while (flag[j] && turn = = j);
critical section
flag[i] = false;
remainder section
} while (true);
Peterson’s Solution (Cont.)
do {
acquire lock
critical section
release lock
remainder section
} while (TRUE);
test_and_set() Instruction
FALSE
test_and_set() Instruction
test_and_set() Instruction
test_and_set() Instruction
test_and_set() Instruction
test_and_set() Instruction
test_and_set() Instruction
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 using compare_and_swap
acquire() {
while (!available)
; /* busy wait */
available = false;
}
release() {
available = true;
}
do {
acquire lock
critical section
release lock
remainder section
} while (true);
Semaphore
Synchronization tool that provides more sophisticated ways (than Mutex
locks) for process to synchronize their activities.
Semaphore S – integer variable
Can only be accessed via two indivisible (atomic) operations
wait() and signal()
Originally called P() and V()
[P stands for Proberen meaning “to test”
V stands for Verhogen meaning “to increment”]
Definition of the wait() operation
wait(S) {
while (S <= 0)
; // busy wait
S--;
}
Definition of the signal() operation
signal(S) {
S++;
}
Semaphore Usage
Counting semaphore – integer value can range over an
unrestricted domain
Used to control access to a given resource consisting of a finite number
of resources. The semaphore is initialized to the number of resources.
Binary semaphore – integer value can range only between 0 and 1
Same as a mutex lock
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 with no Busy waiting
The definition of wait() and signal() operation can suffer from busy
waiting.
Two operations:
block – place the process invoking the operation on the waiting
queue associated with the semaphore
wakeup – remove one of processes in the waiting queue and
place it in the ready queue
Definition of Semaphore:
typedef struct{
int value;
struct process *list;
} semaphore;
Semaphore Implementation with no Busy waiting
signal(semaphore S) {
S->value++;
if (S->value <= 0) {
remove a process P from waiting list (S->list);
wakeup(P);
}
}
After increasing the counter by 1, if the new counter value is not positive, then
remove a process P from the waiting list,
resume the execution of process P, and return
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 {
wait(rw_mutex);
...
/* writing is performed */
...
signal(rw_mutex);
} while (true);