OS_7__Synchronization_Tools
OS_7__Synchronization_Tools
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
Race Condition
• Processes P0 and P1 are creating child processes using the fork()
system call
• Race condition on kernel variable next_available_pid which
represents the next available process identifier (pid)
while(true){
turn = i;
while(turn == j)
;
/* critical section */
turn = j;
/* remainder section */
}
Correctness of the Software Solution
flag[i] = true;
turn = j;
while (flag[j] && turn == j)
;
/* critical section */
flag[i] = false;
/* remainder section */
}
Correctness of Peterson’s Solution
• Thread 1 performs
while (!flag)
;
print x
• Thread 2 performs
x = 100;
flag = true
flag = true;
x = 100;
• Definition
• Properties
– Executed atomically
– Returns the original value of passed parameter
– Set the new value of passed parameter to true
Solution using test_and_set()
• Shared boolean variable lock, initialized to false
• Solution:
do {
while (test_and_set(&lock))
; /* do nothing */
/* critical section */
lock = false;
/* remainder section */
} while (true);
• Properties
– Executed atomically
– Returns the original value of passed parameter value
– Set the variable value the value of the passed parameter new_value but
only if *value == expected is true. That is, the swap takes place only
under this condition.
Solution using compare_and_swap
while (true)
{
while(compare_and_swap(&lock, 0, 1) != 0)
; /* do nothing */
/* critical section */
lock = 0;
/* remainder section */
}
Atomic Variables
• Typically, instructions such as compare-and-swap are
used as building blocks for other synchronization
tools.
• One tool is an atomic variable that provides atomic
(uninterruptible) updates on basic data types such as
integers and booleans.
• For example:
– Let sequence be an atomic variable
– Let increment() be operation on the atomic variable
sequence
– The Command:
increment(&sequence);
ensures sequence is incremented without interruption:
Mutex Locks
• Previous solutions are complicated and generally inaccessible to
application programmers
• OS designers build software tools to solve critical section problem
• Simplest is mutex lock
– Boolean variable indicating if lock is available or not
• Protect a critical section by
– First acquire() a lock
– Then release() the lock
• Calls to acquire() and release() must be atomic
– Usually implemented via hardware atomic instructions such as
compare-and-swap
• But this solution requires busy waiting
– This lock therefore called a spinlock
Solution to CS Problem Using Mutex Locks
while (true) {
acquire lock
critical section
release lock
remainder section
}
Semaphore
• Synchronization tool that provides more sophisticated ways (than Mutex locks)
for processes to synchronize their activities.
wait(S)
{
while (S <= 0); // busy wait
S--;
}
signal(S)
{
S++;
}
Semaphore (Cont.)
• 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 implement a counting semaphore S as a binary
semaphore
• With semaphores we can solve various synchronization
problems
Semaphore Usage Example
• Solution to the Critical Section (CS) Problem using semaphore:
• Waiting queue
typedef struct
{
int value;
struct process *list;
}semaphore;
Implementation with no Busy waiting (Cont.)
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);
}
}
Liveness
P0 P1
wait(S); wait(Q);
wait(Q); wait(S);
... ...
signal(S); signal(Q);
signal(Q); signal(S);