More Synchronization, Semaphores Vivek Pai / Kai Li Princeton University

advertisement
More Synchronization,
Semaphores
Vivek Pai / Kai Li
Princeton University
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);
2
Cost of Protecting a Shared
Variable

Making lock system call



System call in kernel





3
Pushing parameter, sys call # onto stack
Generating trap/interrupt to enter 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
What is Lock Contention?

Competition for a lock




Question: what do these combinations do?




4
Uncontended = rarely in use by someone else
Contended = often used by someone else
Held = currently in use by someone
Spinning on low-contention lock
Spinning on high-contention lock
Blocking on low-contention lock
Blocking on high-contention lock
Things to Ponder
5

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?
What If You Have the Following

Test-and-set – works at either user or
kernel

System calls for block/unblock


6
Block takes some token and goes to sleep
Unblock “wakes up” a waiter on token
User-Level Acquire/Release using
Block and Unblock
Acquire(lock) {
while (!TAS(lock))
Block( lock );
}


7
Release(lock) {
lock = 0;
Unblock( lock );
}
In what scenarios is this scheme
appropriate?
Where should it not be used?
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);
}
8
• Up or “V”
– Atomic
– Increment semaphore by 1
wake up a waiting P if any
V(s) {
if (++s <= 0)
Unblock(s);
}
Bounded Buffer
(Consumer-Producer)

Example:

grep vivek access.log | more
Producer
9
Consumer
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);
}
}
10
}
}
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)
}
11
V(s) {
Acquire(s.mutex);
if (++s.value <= 0)
Unblock(s);
else
Release(s.mutex)
}
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);
}

Kotulski (1988)


12
V(s) {
Acquire(s.mutex);
if (++s.value <= 0)
Release(s.delay);
Release(s.mutex);
}
Two processes call P(s) (when s.value is 0) and
preempted after Release(s.mutex)
Two other processes call V(s)
Hemmendinger’s Solution (1988)
P(s) {
Acquire(s.mutex);
if (--s.value < 0) {
Release(s.mutex);
Acquire(s.delay);
}
Release(s.mutex);
}


13
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
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
14
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);
}
Correct but a complex solution
15
V(s) {
Acquire(s.mutex);
if (++s.value <= 0) {
s.wakecount++;
if (s.wakecount == 1)
Release(s.delay);
}
Release(s.mutex);
}
Hsieh’s Solution (1989)
P(s) {
Acquire(s.delay);
Acquire(s.mutex);
if (--s.value > 0)
Release(s.delay);
Release(s.mutex);
}


16
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
Download