360 likes | 514 Views
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
E N D
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
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
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
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
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);
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
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
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?
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
User-Level Acquire/Release using Block and Unblock • In what scenarios is this scheme appropriate? • Where should it not be used?
Semaphores (Dijkstra, 1965) • Up or “V” • Atomic • Increment semaphore by 1 wake up a waiting P if any • Down or “P” • Atomic • Wait for semaphore to become positive and then decrement by 1 P(s) { if (--s < 0) Block(s); } V(s) { if (++s <= 0) Unblock(s); }
Bounded Buffer (Consumer-Producer) • Example: • grep vivek access.log | more Producer Consumer
Bounded Buffer w/ Semaphores mutex = 1 emptyCount = N; fullCount = 0; producer() { while (1) { produce an item P(emptyCount); P(mutex); put the item in buffer V(mutex); V(fullCount); } } consumer() { while (1) { P(fullCount); P(mutex); take an item from buffer V(mutex); V(emptyCount); consume the item } }
Implementing General Semaphores • Need a mutex for each semaphore • Block and Unblock need to release mutex after entering their critical section V(s) { Acquire(s.mutex); if (++s.value <= 0) Unblock(s); else Release(s.mutex) } P(s) { Acquire(s.mutex); if (--s.value < 0) Block(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) • Two processes call P(s) (when s.value is 0) and preempted after Release(s.mutex) • Two other processes call V(s) V(s) { Acquire(s.mutex); if (++s.value <= 0) Release(s.delay); Release(s.mutex); }
Hemmendinger’s Solution (1988) P(s) { Acquire(s.mutex); if (--s.value < 0) { Release(s.mutex); Acquire(s.delay); } 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 V(s) { Acquire(s.mutex); if (++s.value <= 0) Release(s.delay); else Release(s.mutex); }
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
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
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
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
Answer: All of the Above • Up to 6.5 feet long • Thought to alert for the presence of crocodiles
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
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
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?
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
Monitor Hides Mutual Exclusion • Procedures are mutually exclusive Shared data Queue of waiting processes trying to enter the monitor ... procedures
Condition Variables in A Monitor Queues associated with x, y conditions • Wait( condition ) • Block on “condition” • Signal( condition ) • Wakeup a blocked process on “condition” • Conditions are not “sticky” x y Shared data ... Entry queue operations
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;
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
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
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
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”
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
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
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