Threads & Synchronization, Conclusion Vivek Pai Nov 20, 2001

advertisement
Threads & Synchronization,
Conclusion
Vivek Pai
Nov 20, 2001
Mechanics
 Read Birrell’s paper – future use
I’ll send out a link via e-mail
 Next time: basic networking, IPC
Some readings already listed, maybe more in e-mail
 Now: wrap up threads, critical sections
 Also – quiz 3 graded, working on current grades
2
Big Picture on Synchronization
 Why do we need it?
We have multiple things running
They perform read/write sharing of data
 Can we avoid synchronization?
Eliminate shared data
Perform read-only sharing
Eliminate parallel execution
3
Synchronization Primitives
 Why so many?
Different mechanisms for different situations
Convenient for programmers
 Can we have fewer?
Most can be built from “first principles”
Reinvention error-prone and time-consuming
 How do you decide?
Analysis or Idiom
4
Primitives and Purposes

Locks
One holder, maybe lots of waiters

Semaphores
Possibly lots of holders, lots of waiters

Barriers
Wait until everyone catches up

Condition variables
Waiters block, signal everyone when some condition occurs

Monitors
Expresses synchronization at a high level
5
Continuing on Synchronization
So far, we’ve seen
 “Spinning” on lock during entire critical section
 Disabling interrupts for critical section (bad)
 Queue associated with each lock & blocking
 System calls for locking – possibly blocking
Since system calls exist, is everything solved?
Assume shared variable “count”
Lock(&mutex); count++; Unlock(&mutex);
6
Cost of Protecting a Shared Variable

Making lock system call
Pushing parameter, sys call # onto stack
Generating trap/interrupt to enter kernel

System call in kernel
Jump to appropriate function in kernel
Verify process passed in valid pointer to mutex
Do locking operation, block process if needed
Actually change count – load/modify/store
 System call again to release mutex

7
What is Lock Contention?

Competition for a lock
Uncontended = rarely in use by someone else
Contended = often used by someone else
Held = currently in use by someone

Question: what do these combinations do?
Spinning on low-contention lock
Spinning on high-contention lock
Blocking on low-contention lock
Blocking on high-contention lock
8
Things to Ponder
 If critical section is just “count++;”, what is the
overhead of the synchronization
 Is there some other way of doing this?
 What if you don’t know how long the critical
section will be?
9
What If You Have the Following
 Test-and-set – works at either user or kernel
 System calls for block/unblock
Block takes some token and goes to sleep
Unblock “wakes up” a waiter on token
10
User-Level Acquire/Release
using Block and Unblock
 In what scenarios is this scheme appropriate?
 Where should it not be used?
11
Semaphores (Dijkstra, 1965)

Down or “P”
Atomic
Wait for semaphore to
become positive and then
decrement by 1
P(s) {
if (--s < 0)
Block(s);
}
• Up or “V”
– Atomic
– Increment semaphore by 1
wake up a waiting P if any
V(s) {
if (++s <= 0)
Unblock(s);
}
12
Bounded Buffer
(Consumer-Producer)

Example:
grep vivek access.log | more
Producer
Consumer
13
Bounded Buffer w/ Semaphores
mutex = 1
emptyCount = N;
fullCount = 0;
producer() {
while (1) {
produce an item
P(emptyCount);
consumer() {
while (1) {
P(fullCount);
P(mutex);
take an item from buffer
V(mutex);
P(mutex);
put the item in buffer
V(mutex);
V(emptyCount);
consume the item
V(fullCount);
}
}
}
}
14
Implementing General Semaphores
 Need a mutex for each semaphore
 Block and Unblock need to release mutex after
entering their critical section
P(s) {
Acquire(s.mutex);
if (--s.value < 0)
Block(s);
else
Release(s.mutex)
}
V(s) {
Acquire(s.mutex);
if (++s.value <= 0)
Unblock(s);
else
Release(s.mutex)
}
15
Implement General Semaphores
with Acquire/Release
P(s) {
Acquire(s.mutex);
if (--s.value < 0) {
Release(s.mutex);
Acquire(s.delay);
} else
Release(s.mutex);
}

V(s) {
Acquire(s.mutex);
if (++s.value <= 0)
Release(s.delay);
Release(s.mutex);
}
Kotulski (1988)
Two processes call P(s) (when s.value is 0) and preempted
after Release(s.mutex)
Two other processes call V(s)
16
Hemmendinger’s Solution (1988)
P(s) {
Acquire(s.mutex);
if (--s.value < 0) {
Release(s.mutex);
Acquire(s.delay);
}
Release(s.mutex);
}
V(s) {
Acquire(s.mutex);
if (++s.value <= 0)
Release(s.delay);
else
Release(s.mutex);
}
The idea is not to release s.mutex and turn it over
individually to the waiting process
 P and V are executing in lockstep

17
Kearns’s Solution (1988)
P(s) {
Acquire(s.mutex);
if (--s.value < 0) {
Release(s.mutex);
Acquire(s.delay);
Acquire(s.mutex);
if (--s.wakecount > 0)
Release(s.delay);
}
Release(s.mutex);
}
V(s) {
Acquire(s.mutex);
if (++s.value <= 0) {
s.wakecount++;
Release(s.delay);
}
Release(s.mutex);
}
Two Release( s.delay) calls are also possible
18
Hemmendinger’s Correction
(1989)
P(s) {
Acquire(s.mutex);
if (--s.value < 0) {
Release(s.mutex);
Acquire(s.delay);
Acquire(s.mutex);
if (--s.wakecount > 0)
Release(s.delay);
}
Release(s.mutex);
}
V(s) {
Acquire(s.mutex);
if (++s.value <= 0) {
s.wakecount++;
if (s.wakecount == 1)
Release(s.delay);
}
Release(s.mutex);
}
Correct but a complex solution
19
Hsieh’s Solution (1989)
P(s) {
Acquire(s.delay);
Acquire(s.mutex);
if (--s.value > 0)
Release(s.delay);
Release(s.mutex);
}
V(s) {
Acquire(s.mutex);
if (++s.value == 1)
Release(s.delay);
Release(s.mutex);
}
 Use Acquire(s.delay) to block processes
 Correct but still a constrained implementation
20
Definition Time
 What’s a semaphore?
OED says signaling mechanism using flags,
especially used for rail and sea
 What’s a monitor? Is it
A device to watch a signal without disrupting it
A person who watches, enforces, etc
A programming-language construct for exclusion
A giant lizard
21
Answer: All of the Above
 Up to 6.5 feet long
 Thought to alert for the presence of crocodiles
22
Natural Habitat of the Monitor
 The Mesa system
Xerox PARC (Palo Alto Research Center)
– Cool place
– Lots of neat stuff developed there
 Digital SRC
Exodus of people from PARC
Language, parallelism focus
23
Motivation
Enqueue(q, item)
{
Acquire(mutex);
put item into q;
Release(mutex);
}

Dequeue(q)
{
Acquire(mutex);
take an item from q;
Release(mutex);
return item;
}
What we want
Dequeue(q) blocks until q is not empty

Semaphores are difficult to use: orders are important
24
Think About Critical Sections
 What are they?
Pieces of code in the parallel environment
 What makes code a critical section?
Correctness constraints, ultimately
But really, what makes code a critical section?
 Is there some way to take advantage of this?
If so, when?
25
Answer – Push It To The Compiler
 Easier on programmer
 Compiler gets it right once
Programmer gets it wrong often
 Information available at compile-time
 Small amount of programmer annotation
26
Monitor Hides Mutual Exclusion
 Procedures are
mutually exclusive
Shared
data
Queue of waiting processes
trying to enter the monitor
...
procedures
27
Condition Variables in A Monitor

Wait( condition )
Block on “condition”

Signal( condition )
Wakeup a blocked process
on “condition”

Queues
associated
with x, y
condition
s
x
y
Shared
data
Conditions are not “sticky”
...
Entry queue
operations
28
Producer-Consumer with
Monitors
monitor ProdCons
condition full, empty;
procedure Enter;
begin
if (the queue is full)
wait(full);
put item into buffer;
if (only one item)
signal(empty);
end;
procedure Remove;
begin
if (buffer is empty)
wait(empty);
remove an item;
if (buffer was full)
signal(full);
end;
procedure Producer
begin
while true do
begin
produce an item
ProdCons.Enter();
end;
end;
procedure Consumer
begin
while true do
begin
ProdCons.Remove();
consume an item;
end;
end;
29
Wow, This Looks Like Cake!
 Well, the language/compiler has to support it
 One problem – what happens on wakeup?
Only one thing can be inside monitor
Wakeup implies signaller, waiter in monitor
30
Options of the Signaler
Exit the monitor (Hansen)
 Relinquishes control to the awaken process and
suspend the current one (Hoare)

Complex if the signaler has other work to to
To make sure there is no work to do is difficult because the
signal implementation is not aware how it is used
It is easy to prove things

Continues its execution (Mesa)
Easy to implement
But, the condition may not be true when the awaken process
actually gets a chance to run
31
Mesa Style “Monitor”
(Birrell’s Paper)
Acquire and Release
 Wait( lock, condition )

Atomically unlock the mutex and enqueued on the condition
variable (block the thread)
Re-lock the lock when it is awaken

Signal( condition )
Noop if there is no thread blocked on the condition variable
Wake up at least one if there are threads blocked

Broadcast( condition )
Wake up all
32
Example
 Add an item to the queue
Acquire( mutex );
add an item to the queue;
Signal( nonEmptyCond );
Release( mutex );
 Remove an item from a queue
Acquire( mutex );
while ( queue is empty )
Wait( mutex, nonEmptyCond );
remove an item;
Release( mutex );
 Question: Can “while” be replaced by “if”
33
Mesa-Style vs. Hoare-Style
Monitor
 Mesa-style
Signaller keeps lock and CPU
Waiter simply put on ready queue, with no special
priority
 Hoare-style
Signaller gives up lock and waiter runs immediately
Waiter gives lock and CPU back to signaller when it
exits critical section or if it waits again
34
Condition Variables Primitives

Wait( mutex, cond )
Enter the critical section
(min busy wait)
Release mutex
Put my PCB to cond’s
queue
Call scheduler
Exit the critical section
Acquire mutex
• Signal( cond )
– Enter the critical section
(min busy wait)
– Wake up one PCB in
cond’s queue
– Exit the critical section
35
Are Monitors Alive Today?
 Actual monitors were fairly dead
Java resurrected them
 What killed the monitor?
Man encroached on their environment – just kidding
Language support a real dead weight
C kills everything not-C
 But they still exist, sort of
Condition variables, etc., used heavily
36
Download