6 Syncronization
6 Syncronization
Chapter 5
Process Synchronization
Cooperating processes may share data via
shared address space (heap) by using threads
shared memory objects
files
Producer Consumer
Shared Buffer
at most BUFFER_SIZE items
Producer and Consumer Code
Producer ConSUMER
Race condition
The situation where several processes
access and manipulate shared data concurrently.
The final value of the shared data depends upon which
process finishes last.
register1 = count
register1 = register1 + 1
count = register1
count-- could be implemented as
register2 = count
register2 = register2 - 1
count = register2
} while (TRUE)
Entry section will allow only one process to enter and execute critical section
code.
Solution to Critical-Section Problem
1. Mutual Exclusion - If process Pi is executing in its critical section, then no
other processes can be executing in their critical sections
3. Bounded Waiting - A bound must exist on the number of times that other
processes are allowed to enter their critical sections after a process has made
a request to enter its critical section and before that request is granted //No
starvation of a process
Assume that each process executes at a nonzero speed
No assumption concerning relative speed of the n processes
Applications and Kernel
Multiprocess applications sharing a file or shared memory segment may face
critical section problems.
Multithreaded applications sharing global variables may also face critical
section problems.
Similarly, kernel itself may face critical section problem. It is also a program.
It may have critical sections.
Kernel Critical Sections
While kernel is executing a function x(), a hardware interrupt may arrive and
interrupt handler h() can be run. Make sure that interrupt handler h() and x()
do not access the same kernel global variable. Otherwise race condition may
happen.
While a process is running in user mode, it may call a system call s(). Then
kernel starts running function s(). CPU is executing in kernel mode now. We
say the process is now running in kernel mode (even though kernel code is
running).
Therefore, we need solve synchronization and critical section problem for the
kernel itself as well. The same problem appears there as well.
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]
The variable turn indicates whose turn it is to enter the critical section.
The flag array is used to indicate if a process is ready to enter the critical
section. flag[i] = true implies that process Pi is ready!
Algorithm for Process Pi
do {
flag[i] = TRUE;
turn = j; entry section
while (flag[j] && turn = = j);
critical section
flag[i] = FALSE; exit section
remainder section
} while (1);
flag[ ]
Shared Variables turn
Bakery Algorithm
Critical Section for N processes
Before entering its critical section, a process receives a number (like in a
bakery). Holder of the smallest number enters the critical section.
The numbering scheme here always generates numbers in increasing order
of enumeration; i.e., 1,2,3,3,3,3,4,5...
If processes Pi and Pj receive the same number, if i < j, then Pi is served
first; else Pj is served first (PID assumed unique).
Choosing a number
max (a0,…, an-1) is a number k, such that k ai for i = 0, …, n – 1
Notation for lexicographical order (ticket #, PID #)
(a,b) < (c,d) if a < c or if a == c and b < d
Shared Data:
Boolean choosing[n] initialized to FALSE
Integer number[n] initialized to 0
Bakery Algorithm
do {
choosing[i] = TRUE;
number[i] = max(number[0], …, number[n – 1]) +1;
choosing[i] = FALSE;
critical section
number[i] = 0;
remainder section
} while (TRUE);
Synchronization Terminology
Atomic Operation: is an indivisible operation that always runs to
completion or not at all. It cannot be stopped, or its state altered by
someone else in the middle. It’s a fundamental building block . If no
atomic operations, then have no way for threads to work together
What are the correctness properties for the “Too much milk” problem???
Only one person buys milk at a time
Someone buys milk if you need it
Restrict ourselves to use only atomic load and store operations as building blocks
Too Much Milk: Solution #1
Leave a note before buying (a version of “lock”)
Remove note after buying (a version of “unlock”)
Don’t buy any milk if there is note (wait)
Does it work?
Still too much milk but only occasionally!
Thread context switched after checking milk & note but before buying
milk!
Solution makes problem worse since fails intermittently
To Much Milk Solution #2
How about using labeled notes so we can leave note before checking the
milk?
Thread A Thread B
leave note A; leave note B;
if (noNote B) { if (noNoteA){
if (noMilk){ if (noMilk){
buy Milk; buy Milk;
} }
} }
remove note A; remove note B;
Locks: one process holds a lock at a time, does its critical section,
releases lock
Thread A Thread B
Lock.Acquire(); Lock.Acquire();
if (noMilk) if (noMilk)
buy milk; buy milk;
Lock.Release(); Lock.Release();
A semaphore is
a variable or abstract data type
that provides a simple but useful abstraction
for controlling access,
by multiple processes,
to a common resource
in a parallel programming or a multi user
environment.
Semaphores
Synchronization tools that does not require busy waiting (generalized
locks).
Less complicated entry and exit sections when semaphores are used
signal(S):
if there is a process waiting
wake it up and return
else
S++ and return
Comments
Wait body and signal body have to be executed atomically: one
process at a time. Hence the body of wait and signal are critical
sections to be protected by the kernel.
Note that when Wait() causes the process to block, the operation is
nearly finished (wait body critical section is done).
That means another process can execute wait body or signal body
Semaphore as General Synchronization Tool
Binary semaphore – integer value can range from 0 to 1; simpler to
implement
Guarantees mutually exclusive access to a resource (only one process is in
the critical section at a time)
It is initialized to free (value = 1)
Also known as mutex locks
do {
Wait(S); //wait until semaphore S is available
<critical section>
Signal(S); //signals other processes that semaphore S is free
<reminder section>
} while (TRUE);
Semaphore S; //initialized to 1
Thread A Thread B
do{ do{
Wait(S); Wait(S);
if (noMilk) if (noMilk)
buy milk; buy milk;
Signal(S); Signal(S);
}while (1); }while (1);
P0 P1
… …
S1; Assume we definitely want to
S2;
…. have S1 executed before S2.
….
semaphore x = 0; // initialized to 0
P0 P1
… …
S1; wait (x);
Solution: signal (x); S2;
…. ….
Uses of Semaphore: synchronization
Buffer is an array of BUF_SIZE Cells (at most BUF_SIZE items can be put)
Producer Consumer
do { do {
// produce item wait (Full_Cells);
…
put item into buffer ….
remove item from buffer
.. ..
signal (Full_Cells); …
} while (TRUE);
} while (TRUE);
wait() {…} signal() {…}
Kernel
Semaphore Full_Cells = 0; // initialized to 0
Consumer/Producer is Synchronized
Full_Cells
BUF_SIZE
Producer
Sleeps
0 time
Consumer
Sleeps
Consumer/Producer is Synchronized
BUF_SIZE
times
all items consumed (Ct)
Usage: Resource Allocation
A library has 10 study rooms, to be used by one student at a time. At most 10
students can use the rooms concurrently. Additional students that need to use
the rooms need to wait until a room is free.
Students must request a room from the front counter and return to the counter
when finished using a room. The clerk does not keep track of which room is
occupied or who is using it. When a student requests a room, the clerk
decreases this number. When a student releases a room, the clerk increases this
number. The front desk represents a semaphore, the rooms are the resources,
and the students represent processes. How can we code those processes?
Solution:
One of the processes creates and initializes a semaphore to 10.
Semaphore x = 10;
wait (x);
…
….use one instance Each process has to be
of the resource… coded in this manner.
…
signal (x);
Exercise
X and Y are shared semaphores. The following 3 pseudo-coded threads
are started. What is the output and also mention the updated values of X
and Y after completion of every Thread? ? X = 0, Y = 1
wait(S) signal(S)
{ {
while (S ≤ 0); // busy wait S++;
S--; }
}
Answer:
CAB
X = 1, Y = 0
Exercise
Write a pseudo code to synchronize processes A, B, C and D by using
semaphores so that process B must finish executing before A starts, and process
A must finish before process C or D starts. Show your solution. You should
assume three semaphores X, Y and Z and all initialized to zero.
Solution:
X=Y=Z=0
Readers-Writers Problem
Dining-Philosophers Problem
Bounded-Buffer Problem
N buffers, each can hold one item
Semaphore mutex initialized to the value 1
Semaphore full initialized to the value 0
Semaphore empty initialized to the value N
buffer
producer consumer
full = 4
empty = 6
Bounded-Buffer Problem
The structure of the producer process The structure of the consumer process
do { do {
// produce an item in nextp wait (full);
wait (mutex);
wait (empty);
wait (mutex); // remove an item from
// buffer to nextc
// add the item to the buffer
signal (mutex);
signal (mutex); signal (empty);
signal (full);
// consume the item in nextc
} while (TRUE);
} 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. If a writer is writing then
no reader is allowed to read it.
reader
writer
reader
Data Set
reader
writer
writer reader
Readers-Writers Problem
Shared Data
Data set
Integer readcount initialized to 0
Number of readers reading the data at the moment
Semaphore mutex initialized to 1
Protects the readcount variable
(multiple readers may try to modify it)
Semaphore wrt initialized to 1
Protects the data set
(either writer or reader(s) should access data at a time)
Readers-Writers Problem
a resource
a process
But lots of real resource allocation problems look like this. If we can solve this
problem effectively and efficiently, we can also solve the real problems.
// eat
signal ( chopstick[i] );
signal (chopstick[ (i + 1) % 5] );
// think
} while (TRUE);
What is the problem with this algorithm?
This solution provides concurrency but may result in deadlock.
Dinning-Philosophers Problem Variations
Represent each chopstick by a semaphore
No two neighbors can eat simultaneously
Can still create a deadlock
Deadlock Handling
Allow at most 4 philosophers to sit simultaneously on the table.
Allow to pick up the chopsticks only if both are available (pick up must
be in critical section)
An odd philosopher picks up first his left chopstick and then his right,
whereas an even philosopher picks up his right chopstick first and then his
left.
Problems with Semaphores
Incorrect use of semaphore operations:
Example: Therac-25
Machine for radiation therapy
Software control of electron
accelerator and electronXray
production
Software control of dosage
Software errors caused the
death of several patients
A series of race conditions on
shared variables and poor
software design
“They determined that data entry speed during editing was the key factor
in producing the error condition: If the prescription data was edited at a
fast pace, the overdose occurred.”
Space Shuttle Example
Original Space Shuttle launch aborted 20 minutes before scheduled launch
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);
}
}