You are on page 1of 20

Chapter 7

Synchronization
Examples
Edited by
Ghada Mohammed S AlOsaimi
CS Lecturer, CCIS, IMSIU
Outline
 Bounded-Buffer Problem
 Readers-Writers Problem
 Dining-Philosophers Problem

Process
Management

Protection and Memory


Security Introduction Management
and Overview

Storage
Management

2
Objectives
 Explain the bounded-buffer, readers-writers, and dining philosophers

synchronization problems.

 Describe the tools used by Linux to solve synchronization problems.

 Illustrate how POSIX can be used to solve process synchronization problems.

3
Classical Problems of Synchronization

 Classical problems used to test newly-proposed synchronization schemes


 Bounded-Buffer Problem
 Readers and Writers Problem
 Dining-Philosophers Problem

 In our solutions to the problems above, we use semaphores for synchronization, since that is the traditional way to present
such solutions
 However, actual implementations of these solutions could use mutex locks in place of binary semaphores

4
Bounded-Buffer Problem

 Sometimes, it is called producer-consumer problem and commonly used to illustrate the power of synchronization primitives
 The idea is that a producer tries to insert data into an empty slot of the buffer and a consumer tries to remove data from a
filled slot in the buffer. The problem is occurred when:
 Multiple producer threads
 Multiple consumer threads
 One bounded buffer with N entries
 All threads modify the same buffer

5
Bounded-Buffer Problem (cont.)

 In order to implement the producer consumer problem correctly, the following constraints should be satisfied:
 Consumer must wait for producer to fill the shared buffer, if none in the buffer (scheduling constraint)
 Producer must wait for consumer to empty the buffer, if the buffer is full (scheduling constraint)
 Only one thread can manipulate the buffer queue at a time (mutual exclusion)
 The solution is accomplished by using Semaphore values, in our problem, the producer and consumer processes share the
following data structures:

 n buffers, each can hold one item


 Semaphore mutex initialized to the value 1 → provides mutual exclusion for accesses to the buffer

6  Semaphore full initialized to the value 0 → count the number of full buffers
 Semaphore empty initialized to the value n → count the number of empty buffers
Bounded-Buffer Problem - Process Structure

Producer Consumer
while (true) { while (true) {
... /* wait until full > 0 and then decrement full’*/
/* produce an item in next_produced */
wait(full);
wait(mutex); // acquire lock
/* wait until empty > 0 and then decrement 'empty’*/
...
wait(empty); /* remove an item from buffer to next_consumed */
wait(mutex); // acquire lock ...
... signal(mutex); // release lock
/* add next produced to the buffer */
signal(empty); // increment full

...
signal(mutex); // release lock
/* consume the item in next consumed */
signal(full); // increment full
...
} }
7
POSIX Semaphore – Bounded-Buffer Problem

8
Readers-Writers Problem

 A data set is shared among a number of concurrent processes, some of these processes may want only to read the data set,
whereas others may want to update (that is, read and write)
 Readers – only read the data set; they do not perform any updates
 Writers – can both read and write
 Obviously, if two readers access the shared data simultaneously, no adverse effects will result
 However, if a writer and some other process (either a reader or a writer) access the database simultaneously, chaos may
happen!
 To ensure that these difficulties do not arise, we require that the writers have exclusive access to the shared database while
writing to the database or data set
 This synchronization problem is referred to as the readers-writers problem

9
Readers-Writers Problem Variations

 Several variations of how readers and writers are considered – all involve some form of priorities
 First variation – no reader kept waiting unless writer has permission to use shared object, in other words, no reader
should wait for other readers to finish simply because a writer is waiting
 Second variation – once writer is ready, it performs the write ASAP, in other words, if a writer is waiting to access the
object, no new readers may start reading
 Both may have starvation leading to even more variations
 In the first case, writers may starve
 In the second case, readers may starve
 Problem is solved on some systems by kernel providing reader-writer locks

10
Readers-Writers Problem Variations (cont.)

 In this lecture, we will discuss the solution of the first readers-writers problem, and in your textbook you will find more
references on solving the second readers-writers problem. First variation shares following data structures:

 Semaphore rw_mutex initialized to 1 → is common to both reader and writer processes

 Semaphore mutex initialized to 1 → is used to ensure mutual exclusion when the variable read count is updated

 Integer read_count initialized to 0 → keeps track of how many processes are currently reading the object

11
Readers-Writers Problem - Process Structure

Reader
while (true){
wait(mutex); //reader acquire lock to update count
read_count++;
if (read_count == 1)
wait(rw_mutex); // to ensure no writers
Writer
signal(mutex); //reader release lock to update count
while (true) {
wait(rw_mutex); //writer acquire lock ...
/* reading is performed */
...
/* writing is performed */ ...
... wait(mutex); //reader acquire lock to update count
read count--;
signal(rw_mutex); //writer release lock if (read_count == 0)
} signal(rw_mutex); // enable no writers

signal(mutex); //reader release lock to update count


12
}
POSIX Semaphore – Readers-Writers Problem

13
Dining-Philosophers Problem

 Consider five philosophers who spend their lives thinking and eating
 The philosophers share a circular table surrounded by five chairs, each belonging to one philosopher
 In the center of the table is a bowl of rice, and the table is laid with five single chopsticks
 When a philosopher thinks, she does not interact with her colleagues
 From time to time, a philosopher gets hungry and tries to pick up the two chopsticks that are closest to her (the chopsticks
that are between her and her left and right neighbors)
 A philosopher may pick up only one chopstick at a time, obviously, she cannot pick up a chopstick that is already in the hand
of a neighbor
 When a hungry philosopher has both her chopsticks at the same time, she eats without releasing the chopsticks
 When she is finished eating, she puts down both chopsticks and starts thinking again
 The dining-philosophers problem is considered a classic synchronization problem, it is a simple representation of the need to
allocate several resources among several processes in a deadlock free and starvation free manner
14
Dining-Philosophers Problem - Semaphore Solution

 Shared Data: Bowl of rice - data set + Semaphore chopstick [5] initialized to 1
 The structure of process philosopher i:
while (true){
wait (chopstick[i] );
wait (chopStick[ (i + 1) % 5 ] );

/* eat for awhile */

signal (chopstick[i] );
signal (chopstick[ (i + 1) % 5 ] );

/* think for awhile */

 What is the problem with this algorithm?


15
Dining-Philosophers Problem - Semaphore Solution (cont.)

 Although this solution guarantees that no two neighbors are eating simultaneously, it nevertheless must be rejected because
it could create a deadlock
 Suppose that all five philosophers become hungry at the same time and each grabs her left chopstick
 All the elements of chopstick will now be equal to 0 .
 When each philosopher tries to grab her right chopstick, she will be delayed forever

16
Dining-Philosophers Problem - Semaphore Solution (cont.)

 Several possible solutions to the deadlock problem are the following

 Allow at most four philosophers to be sitting simultaneously at the table

 Allow a philosopher to pick up her chopsticks only if both chopsticks are available (to do this, she must pick them up in
a critical section)

 Use an asymmetric solution that is, an odd numbered philosopher picks up first her left chopstick and then her right
chopstick, whereas an even numbered philosopher picks up her right chopstick and then her left chopstick

17
Monitor

 Monitors are a synchronization construct that were created to overcome problems caused by semaphores as timing errors
 Monitors are abstract data types and contain shared data variables and procedures
 The shared data variables cannot be directly accessed by a process;
 and procedures are required to allow a single process to access the shared data variables at a time
 Only one process can be active in a monitor at a time
 Other processes that need to access the shared variables in a monitor have to line up in a queue and are only provided
access when the previous process release the shared variables
 Using monitor to solve the problem will not lead to deadlock
 But starvation is possible !

18
Dining-Philosophers Problem - Monitor Solution

 To code this solution, we need to distinguish among three states in which we may find a philosopher
enum {THINKING, HUNGRY, EATING} state[5 ]; // shared data
 Philosopher i can set the variable state[ i ] = EATING only if her two neighbors are not eating
(state[(i+4) % 5] != EATING) and (state[(i+1) % 5] != EATING
 We also need to declare condition self[5];
 This allows philosopher i to delay herself when she is hungry but is unable to obtain the chopsticks she needs
 Each philosopher, before starting to eat, must invoke the operation pickup()
 This act may result in the suspension of the philosopher process, after the successful completion of the operation, the
philosopher may eat
 Following this, the philosopher invokes the putdown()operation
 Thus , philosopher i must invoke the operations pickup()and putdown() in the following sequence
DiningPhilosophers.pickup(i);
19 /** EAT **/
DiningPhilosophers.putdown(i);
Dining-Philosophers Problem - Monitor Solution (cont.)

monitor DiningPhilosophers
{
void test (int i) {
enum { THINKING; HUNGRY, EATING) state [5] ;
if ((state[(i + 4) % 5] != EATING) &&
condition self [5];
(state[i] == HUNGRY) &&
(state[(i + 1) % 5] != EATING) ) {
void pickup (int i) {
state[i] = HUNGRY;
state[i] = EATING ;
test(i);
self[i].signal () ;
if (state[i] != EATING) self[i].wait;
}
}
}

void putdown (int i) {


initialization_code() {
state[i] = THINKING;
for (int i = 0; i < 5; i++)
// test left and right neighbors
state[i] = THINKING;
test((i + 4) % 5);
}
test((i + 1) % 5);
20 }
}

You might also like