Lect 4
Lect 4
Synchronization
Lecture 4: Process Synchronization
Background
The Critical-Section Problem
Peterson’s Solution
Synchronization Hardware
Mutex Locks
Semaphores
Monitors
Classic Problems of Synchronization
The dining philosopher, reader- writer and Bounded-Buffer
Problem
2
Objectives
3
Background
4
The Need for Synchronization: Example
• Consider the following real-world scenario, involving 2 roommates:
Bob and Carla
6
Example
const int BUFFER_SIZE=10;
item buffer[BUFFER_SIZE];
int count=0;
Producer Consumer
while (true) { while (true) {
/* produce an item in next
produced */ while (counter == 0)
; /* do nothing */
while (counter == BUFFER_SIZE); next_consumed = buffer[out];
/* do nothing */ out = (out + 1) % BUFFER_SIZE;
buffer[in] = next_produced;
in = (in + 1) % BUFFER_SIZE; counter--;
counter++; /*consume the item in next
} consumed */
7
Race Condition
9
Cont...
The code following the critical section is termed the exit
section.
lets the world know that they are no longer in their critical section.
The rest of the code not included in either the critical
section or the entry or exit sections is termed the
remainder section.
11
Solution to Critical-Section Problem
A solution to the critical-section problem must satisfy the following three
requirements:
12
How synchronization looks like ?
13
Peterson’s Solution
14
Algorithm for Process Pi
do {
flag[i] = true;
turn = j;
while (flag[j] && turn = = j)
// Do nothing ;
critical section
flag[i] = false;
remainder section
} while (true);
15
Peterson’s Solution (Cont.)
16
Synchronization Hardware
Many systems provide hardware support for implementing the critical
section code.
Problems/dis-advantage
Cause problem if user program fails to reset the interrupt
Consider the effect on system time, if it is updated by interrupts
A thread/process that enters an infinite loop in its critical section after
disabling interrupts will never yield(give back) its processor
17
Solution to Critical-section Problem Using Locks
Modern machines provide special atomic hardware instructions
Atomic = non-interruptible
Two of the most known instructions are:
Test_and_set instruction
swap instruction
do {
acquire lock
critical section
release lock
remainder section
} while (TRUE);
18
test_and_set Instruction
Definition:
boolean test_and_set (boolean *target)
{
boolean rv = *target;
*target = TRUE;
return rv:
}
19
Solution using test_and_set()
If the machine supports the test_and_set()instruction, then we can
implement mutual exclusion by declaring a Boolean variable lock,
initialized to false.
Lock=false // implies lock is available
Lock=true // implies lock is taken and someone is in its CS
Example
Shared Boolean variable lock, initialized to FALSE
Solution:
do {
while (test_and_set(&lock))
; /* do nothing */
/* critical section */
lock = false;
/* remainder section */
} while (true);
20
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;
}
Has the following properties
1. Executed atomically
2. Returns the original value of passed parameter “value”
3. Set the variable “value” to the value of the passed parameter
“new_value” but only if “value” ==“expected”. That is, the swap
takes place only under this condition.
21
Solution using compare_and_swap
Shared integer “lock” initialized to 0;
Solution:
do {
while (compare_and_swap(&lock, 0, 1) != 0)
; /* do nothing */
/* critical section */
lock = 0;
/* remainder section */
} while (true);
• Drawbacks
Although these algorithms (using comapre_and_swap() and test_and_set())
satisfy the mutual-exclusion requirement, they do not satisfy the bounded-
waiting requirement.. How/why ?
22
Mutex Locks
The hardware-based solutions are complicated and generally
inaccessible to application programmers
OS designers build software tools to solve critical section problem
Simplest is mutex lock
Protect a critical section by first acquire() a lock then
release() the lock
Boolean variable (available) indicating if lock is available or not
Calls to acquire() and release() must be atomic
Usually implemented via hardware atomic instructions
But this solution requires busy waiting
This lock therefore called a spinlock
23
acquire() and release()
The value for the mutex “available” is TRUE initially
acquire() {
while (!available)
; /* busy wait (SPINLOCK)*/
available = false;;
}
release() {
available = true;
}
Release lock
remainder section
} while (true);
24
Pros and cons of spinlock
the process “spins”while waiting for the lock to become available.
Same is true for solutions using test_and_set and compare_and_swap
Disadvantage
Busy waiting wastes CPUcycles that some other process might be able to
use productively.
Advantage
No context switch is required when a process must wait on a lock
Therefore, when locks are expected to be held for short times, spinlocks
are useful
26
Example
#include <pthread.h> // the main function
int main( void )
#include <unistd.h>
{
pthread_mutex_t lock ; pthread_t thread_ID ;
int shared_data ; // Often shared data is void* exitstatus
more complex than just an int we should int i ;
not allow morethan one thread to modify // Initialize the mutex before trying to use it.
this global */ pthread_mutex_init (&lock , NULL) ;
pthread_create (&thread ID , NULL, thread funct i on , NULL) ;
void *thread_function ( void* arg )
// Try to use the shared data.
{ for ( i = 0 ; i < 10 ; ++i ) {
int i ; s l eep ( 1 ) ;
pthread mutex lock(&l ock ) ;
for ( i = 0 ; i < 1024 *1024; ++i ) {
p r i n t f ( "\rShared integer's value = %d\n" , shared data ) ;
// Access the shared data here. pthread mutex unlock(&l ock ) ;
pthread mutex lock(&l ock ) ; }
Shared_data++; p r i n t f ( "\n" ) ;
pthread_j o i n ( thread ID , &e x i t s t a t u s ) ;
Pthread_mutex_unlock(&l ock ) ;
// Clean up the mutex when we are finished with it.
} pthread_mutex_destroy(&l ock ) ;
return NULL; return 0 ;
} }
27
Semaphore
Synchronization tool that provides more sophisticated ways (than Mutex locks) for
process to synchronize their activities.
28
Semaphore Usage
There are two types of semaphores
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
Semaphore can solve various synchronization problems
29
Problems with Semaphores
Incorrect use of semaphore operations:
30
Monitors
Semaphores cause errors if used incorrectly
One of the solutions to overcome such problems: Monitors
Monitor is a high-level abstraction that provides a convenient and
effective mechanism for process synchronization
Encapsulates data with a set of functions to operate on that data
Local variables only accessible by code within the procedure
Only one process may be active within the monitor at a time
The programmer does not need to code this synchronization
constraint explicitly
If a second thread invokes a monitor procedure when a first thread is
already executing one, it blocks
So the monitor has to have a wait queue…
monitor monitor-name
{
// shared variable declarations
procedure P1 (…) { …. }
32
Important points
Make sure you understand the following points
Race condition
Critical section problem
Solution requirements to CS problem
Mutual exclusiton
Progress
Bounded waiting
Solution to CS problems
H/W solutions
Disabling inerupts
Test_and_set()
Test_and_swap()
S/w Solutions
Peterson’s soltions
Mutex
Semaphore
Monitor
33
End of Chapter 4