You are on page 1of 11

Road Map

 Past:
 What an OS is, why we have them, what they do.
Thread Synchronization 

Base hardware and support for operating systems
Process Management
 Process Scheduling
 Multi-Threading
Dickinson College  Present:
Computer Science 354  Thread Synchronization
 Future:
Spring 2006  Memory management
 Storage management
 Protection and Security
slides courtesy of Professor Grant Braught

Thread Synchronization Motivating Example


 A motivating example What does the ThreadSynch program do?
 Explanation of example
 Terminology
 Synchronization from user’s perspective What output should be produced by the
 The semaphore mechanism ThreadSynch program?
 Synchronization patterns
 Synchronization problems
 Synchronization from the OS’s perspective What output is produced by the
 Within the kernel ThreadSynch program?
 Providing synchronization primitives
 Java synchronization

Inside ThreadSynchExample ThreadSynchExample Execution


Consider the following execution
The machine code produced for the sequence for incThread and decThread:
IncThread and DecThread would contain Assume X is initially 0
some code similar to the following.
incThread: decThread:
IncThread: DecThread: i1: LOAD R0 X
i1: LOAD R0 X d1: LOAD R1 X i2: ADD R0 R0 #1
i2: ADD R0 R0 #1 d2: SUB R1 R1 #1
i3: STORE R0 X d3: STORE R1 X timer d1: LOAD R1 X
interrupt d2: SUB R1 R1 #1
d3: STORE R1 X
timer
interrupt
i3: STORE R0 X

1
Random OS Humor Terminology
Windows Haiku Error Messages Thread synchronization
Mutual Exclusion
Three things are certain: Critical sections
Death, taxes and lost data. Serialization
Guess which has occurred. Race condition
Non-determinism
Having been crashed
The document you're seeking
Must now be retyped.

Thread Synchronization Mutual Exclusion


Recall that threads within a process A mutual exclusion constraint requires that
execute concurrently. two or more events be prevented from
occurring concurrently.
Thread synchronization is the process of Note that mutual exclusion does not impose a
imposing synchronization constraints on specific order on the events.
otherwise concurrently executing threads They may occur in any order, so long as they are
forced to occur sequentially.
causing them to run:
One at a time (mutual exclusion)
In some predetermined order (serialization).

Mutual Exclusion Example Critical Sections


Recall the ThreadSynchExample program:  The sections of each thread that cannot be
permitted to execute concurrently are called
critical sections.
IncThread: DecThread: threadC: threadD:
i1: LOAD R0 X d1: LOAD R1 X c1: doStuff() d1: doSomeStuff()
i2: ADD R0 R0 #1 d2: SUB R1 R1 #1 c2: Y = X + 1 d2: X = X - 1
i3: STORE R0 X d3: STORE R1 X c3: X = 2*Y d3: doOtherStuff()
c4: doMoreStuff()
A mutual exclusion constraint is necessary to
ensure proper execution. Critical Sections

Every section of code that involves an update to a shared


variable should be treated as a critical section and should
have a mutual exclusion constraint imposed upon it.

2
Serialization Serialization Example
A serialization constraint requires that two  Consider two threads, threadA that generates a
value of X and threadB that uses the value of X
or more events be forced to execute in a to calculate the value of Y.
specific order.  Assume: X=1, Y=0 are stored in the address space
shared by the threads.
threadA: threadB:
a1: X = 5 b1: Y = X + 3
a2: Print X b2: Print Y

A serialization constraint is necessary in order to ensure


proper execution.

Race Conditions Non-Determinism


A race condition is any situation in which In the absence of explicit synchronization
the order of execution affects the final constraints, the order of execution of
result. concurrent threads can be non-
The mutual exclusion and serialization deterministic.
examples both contained race conditions. The good
The bad
The ugly

Random OS Humor
Alternative acronyms:
PCMCIA: People Can't Memorize Computer
Industry Acronyms
Thread Synchronization
SCSI: System Can't See It from the User's Perspective
DOS: Defective Operating System
BASIC: Bill's Attempt to Seize Industry
Control

3
Outline Semaphores
 Synchronization mechanisms
 Semaphore A semaphore is an integer with three
 Others differences:
 Synchronization patterns When created, its value must be initialized,
 Signaling thereafter the only operations allowed are
 Rendezvous increment and decrement.
 Mutex When a thread decrements a semaphore, if
 Multiplex the result is negative the thread is blocked on
 Barrier the semaphore.
 Synchronization problems When a thread increments a semaphore, if its
 Producer-Consumer value was negative then one of the threads
 Readers-Writers blocked on the semaphore is woken up.
 Unisex Bathroom

Semaphore Syntax Alternative Syntax


Creating a semaphore with initial value 1:  Decrement:
mySem.decrement();
mySem.P();
mySem = new Semaphore(1);
mySem.decrementAndBlockIfResultIsNegative();

Decrementing a semaphore:
 Increment:
mySem.wait();
mySem.increment();
mySem.V();
Incrementing a semaphore:
mySem.incrementAndWakeAWaitingThreadIfAny();
mySem.signal();

Alternative Mechanisms
In addition to semaphores there are a
variety of other mechanisms that are also
used for thread synchronization:
Synchronization
Locks Patterns
Monitors
Condition Variables

4
Signaling Pattern Rendezvous Pattern
 Rendezvous generalizes signaling to work both
The signaling pattern can be used to ways.
 E.g. Thread A must complete section a1 before
enforce a serialization constraint on two thread B executes section b2 and thread B must
threads. complete section b1 before thread A completes
E.g. Thread A must complete section a1 section a2.
before thread B executes section b2.  How can Rendezvous be implemented using
Shared Data semaphores?
1. mySem = new Semaphore(0);
Thread A Thread B
Thread A Thread B 1. section a1 1. section b1
1. section a1 1. section b1 2. aReady.signal(); 2. bReady.signal();
2. mySem.signal(); 2. mySem.wait(); 3. bReady.wait(); 3. aReady.wait();
3. section a2 3. section b2 4. section a2 4. section b2

Rendezvous Non-Solution Mutex Pattern


 The mutex pattern can be used to enforce a
What’s wrong with the following proposed mutual exclusion constraint on two threads.
 E.g. We know that statement 2 in Thread A should not be
solution to the Rendezvous problem? executed concurrently with statement 2 in Thread B.
Shared Data  How can mutex be implemented using a semaphore?
1. aReady = new Semaphore(0); Shared:
2. bReady = new Semaphore(0); mutex = new Semaphore(1);
Thread A Thread B
Thread A Thread B
1. section a1 1. section b1
1. section a1 1. section b1 2. mutex.wait() 2. mutex.wait();
2. bReady.wait(); 2. aReady.wait(); 3. X = X + 1; 3. X = X - 1;
3. aReady.signal(); 3. bReady.signal(); 4. mutex.signal(); 4. mutex.signal();
4. section a2 4. section b2

Multiplex Pattern Barrier Pattern


 The multiplex pattern generalizes the mutex
pattern to allow only a fixed number of threads to The barrier pattern generalizes the
execute specific sections of code concurrently. rendezvous pattern to cases with N
 E.g. Allow up to N copies of thread A to execute
section a2 concurrently.
threads.
 How can the multiplex pattern be implemented with a E.g. Each copy of thread A must wait for all N
semaphore? copies before executing section a2.
Shared: Could use one semaphore for each thread… but…
multiplex = new Semaphore(N); Thread A
1. section a1
Thread A
2. section a2
1. section a1
2. multiplex.wait();
3. section a2
4. multiplex.signal();

5
Barrier Non-Solution #1 Barrier Solution
What is wrong with the following proposed
solution for the barrier problem? The following code solves the Barrier
problem.
Shared Data Thread A
Shared Data Thread A
1. int N; 1. section a1
1. int N; 1. section a1
2. int count = 0; 2. mutex.wait();
3. mutex = new Semaphore(1); 2. int count = 0; 2. mutex.wait();
3. count++; 3. mutex = new Semaphore(1);
4. barrier = new Semaphore(0); 3. count++;
4. mutex.signal(); 4. barrier = new Semaphore(0); 4. mutex.signal();
5. if (count == N)
5. if (count == N)
6. barrier.signal();
6. barrier.signal();
7. barrier.wait();
7. barrier.wait();
8. section a2 8. barrier.signal();
Turnstile 9. section a2

Barrier Non-Solution #2 Random OS Humor


What is wrong with the following proposed
solution for the barrier problem?
Shared Data Thread A
1. int N; 1. section a1
2. int count = 0; 2. mutex.wait();
3. mutex = new Semaphore(1);
4. barrier = new Semaphore(0); 3. count++;
4. if (count == N)
5. barrier.signal();
6. barrier.wait();
7. barrier.signal();
8. mutex.signal();
9. section a2

Producer-Consumer Problem
 Producer Threads: produce items and adds
them to a shared data structure.
Synchronization  Consumer Threads: remove items from a shared
data structure and processes them.
Problems
Producer Consumer
1. while (true) 1. while (true)
2. section p1 2. section c1
3. pItem = generateItem(); 3. cItem = buffer.get();
4. buffer.add(pItem); 4. processItem(cItem);
5. section p2 5. section c2

6
Producer-Consumer Non-
Producer-Consumer Solution
Solution:
Solution
mutex = new Semaphore(1); What’s wrong with the following code for
items = new Semaphore(0);
the Consumer threads?
Producer: Consumer:
Consumer
1. while (true)
while (true) while (true) 2. section c1
Section p1 Section c1 3. mutex.wait();
4. items.wait();
pItem = generateItem(); items.wait(); 5. cItem = buffer.get();
mutex.wait(); mutex.wait(); 6. mutex.signal();
buffer.add(pItem); cItem = buffer.get(); 7. process(cItem);
8. section c2
items.signal(); mutex.signal();
mutex.signal(); processItem(cItem);
Section p2 Section c2

Producer-Consumer with Producer-Consumer


a Finite Buffer with a Finite Buffer Solution
Solution:
Often the buffer for a producer-consumer mutex = new Semaphore(1);
items = new Semaphore(0);
problem will have a practical limit on its Spaces = new Semaphore(N); // N = # of spaces in buffer.
size.
Producer: Consumer:
while (true) while (true)
What synchronization constraint does this Section p1 Section c1
add? pItem = generateItem(); items.wait();
mutex.wait(); mutex.wait();
buffer.add(pItem); cItem = buffer.get();
How can we augment our solution to deal with items.signal(); mutex.signal();
a finite buffer? mutex.signal(); processItem(cItem);
Section p2 Section c2

Readers-Writers Solution
Readers-Writers Problem Shared Data
1. readers = 0;
 Readers Threads: read information from a 2. mutex = new semaphore(1);
3. roomEmpty = new semaphore(1);
shared data structure (database / variable / file
etc…) Writers: Readers:
 Writers Threads: write information to a shared 1. section w1 1. section r1
2. roomEmpty.wait(); 2. mutex.wait();
data structure. 3. write data 3. readers++;
Writer: Reader: 4. roomEmpty.signal(); 4. if readers == 1
1. section w1 1. section r1 5. section w2 5. roomEmpty.wait();
2. write data 2. read data 6. mutex.signal();
3. section w2 3. section r2
7. read data
8. mutex.wait();
9. readers--;
10. if readers == 0
 What are the synchronization constraints in this 11. roomEmpty.signal();
problem? 12. mutex.signal();

7
Lightswitch Pattern Unisex Bathroom Problem
Shared Data
1. inRoom = 0;
2. mutex = new semaphore(1);  A high-tech startup company can only afford space with
3. light = new semaphore(1); a single bathroom with 3 stalls. A CS major working for
the company proposes to solve the problem of allowing
Thread A: both men and women to use the bathroom using
1. section a1
semaphores.
2. mutex.wait();
3. inRoom++;  Synchronization constraints:
4. if inRoom == 1 // first in…  Men and women cannot be in the bathroom at the same time.
5. light.wait(); // turn on light if off (or block)
6. mutex.signal();  There should never be more than 3 people in the bathroom at once.

7. Critical Section
Man Thread: Woman Thread:
1. while (true) 1. while (true)
8. mutex.wait(); 2. doWork(); 2. doWork();
9. inRoom--; 3. goToBathroom(); 3. goToBathroom();
10. if inRoom == 0 // last out…
11. light.signal(); // turn off light
12. mutex.signal();
13. section a2

Outline
How do semaphores do what they do?
Basic semaphore structure
Synchronization A new critical section!
from the OS Perspective Semaphore implementations
Other synchronization mechanisms

Semaphore Structure Semaphore Implementations


A semaphore consists of an integer To implement a semaphore, it is
variable (value) and two methods for necessary to ensure that execution of the
manipulating it. critical sections is mutually exclusive.
semWait() { semSignal() { Three possibilities:
value = value - 1; value = value + 1; In kernel mode by disabling interrupts.
if (value < 0) { if (value <= 0) {
block calling thread wakeup a thread With hardware support:
} } • Using system calls for blocking.
} } • Without any OS support.

Notice that each semaphore introduces a new


critical section of it own!

8
Semaphores in Kernel Mode Hardware and Mutual Exclusion
Semaphores can be implemented in
Additional instructions can be provided by
kernel mode by disabling and enabling
the hardware to enable mutual exclusion
interrupts.
without disabling interrupts.
semWait() { semSignal() {
disable interrupts disable interrupts
One such instruction is TSL (test and set
value = value - 1; value = value + 1; lock):
if (value < 0) { if (value <= 0) { reg = mm[addr]
enable interrupts wakeup a thread TSL addr mm[addr] = true
block calling thread }
}
return reg
enable interrupts
enable interrupts }
}
TSL like all machine language instructions is
Semaphore creation as well as semWait and executed atomically.
semSignal must be system calls.

Mutual Exclusion with TSL Semaphores with TSL


 Semaphores can be implemented using the TSL
instruction to protect the critical sections.
The TSL instruction can be used to  Each semaphore now has both an integer variable
enforce mutual exclusion as follows: value and a boolean variable lock.
 System calls are required for blocking and waking up
Shared: threads.
boolean lock = false; semWait() { semSignal() {
int x = 0; while(TSL(lock)); while(TSL(lock));
value = value - 1; value = value + 1;
Thread A: Thread B: if (value < 0) { if (value <= 0) {
while(TSL(lock)); while(TSL(lock)); add thread to semaphore queue wakeup a thread
/* Critical section */ /* Critical section */ lock = false; }
x = x + 1; x = x - 1; yield lock = false;
lock = false; lock = false; } else }
lock = false;
}

Busy Waiting Semaphores with Busy-Waiting


With busy waiting, a thread requires CPU If an OS does not provide system calls for
cycles while waiting. blocking threads, semaphores can be
The semaphore implementation using TSL implemented entirely by using busy
uses busy waiting while waiting for the lock. waiting.
semWait() { semSignal() {
This type of waiting is also called a spin-lock. while (value <= 0); while(TSL(lock));
while(TSL(lock)); value = value + 1;
value = value - 1; lock = false;
lock = false; }
}

9
Other Synchronization
Random OS Humor
Mechanisms
"DOS computers manufactured by Semaphores are only one mechanisms for
companies such as IBM, Compaq, Tandy, enforcing synchronization constraints.
and millions of others are by far the most There are several others:
popular, with about 70 million machines in
Locks
use worldwide. Macintosh fans, on the
other hand, may note that cockroaches Monitors
are far more numerous than humans, and Synchronization in Java
that numbers alone do not denote a higher
life form."

— New York Times, November 26,


1991.

Locks Monitors
A lock is a mechanism for enforcing
mutual exclusion. Code within a monitor may only be
executed by a single thread at a time.
Shared:
Lock mutex = new Lock(); monitor <name> {
int x = 0;
<shared variables>
Thread A: Thread B: initialization(<params>) {
mutex.lock(); mutex.lock(); // initialization code
/* Critical section */ /* Critical section */ }
x = x + 1; x = x - 1; procedure P1(<params>) {
mutex.unlock(); mutex.unlock(); // code in P1
}
procedure P2(<params>) {
Locks can be implemented using: // code in P2
Enable/disable interrupts and blocking }

Busy-waiting with TSL }

Condition Variables Semaphore via a Monitor


monitor Semaphore {
Condition variables are a mechanism by int value;
which threads executing within a monitor condition blocked;
initialization(int initVal) {
can block themselves and be woken up by value = initVal;
other threads. }
procedure semWait() {
Condition variables have two operations: value--;
wait() - causes the calling thread to block. if (value < 0)
blocked.wait();
signal() - wakes up a thread blocked on the }
condition variable.
procedure semSignal() {
• Either the signaling thread or the woken thread must wait
value++;
until the other exits the monitor before continuing.
if (value <= 0)
• If no threads are blocked on the condition variable then a blocked.signal();
signal has no effect and the signaling thread continues. }
}

10
Java Semaphore
Java Synchronization
Implementation
public class Semaphore {
Synchronization in Java is accomplished private int value;

with a mechanism similar to a monitor. public Semaphore(int initVal) {


value = initVal;
Every object has a lock and a condition }

variable. public void synchronized semWait() {


value--;
Methods can be declared synchronized. if (value < 0)
try { wait();
Before a thread is permitted to execute a } catch(InterruptedException e) {}
synchronized method, the thread must acquire the }
object’s lock. public void synchronized semSignal() {
value++;
• This happens automatically.
if (value <= 0)
Threads may wait() and notify() the object’s notify();
condition variable. }
}
• notify() is a signal().

11

You might also like