Скачать презентацию CPS 110 Ordering constraints Landon Cox January 22 Скачать презентацию CPS 110 Ordering constraints Landon Cox January 22

e990e9ff0eb3d12a8187d959a373a5f5.ppt

  • Количество слайдов: 41

CPS 110: Ordering constraints Landon Cox January 22, 2008 CPS 110: Ordering constraints Landon Cox January 22, 2008

Too much milk solution leave note. Landon while (note. Melissa){ do nothing } if Too much milk solution leave note. Landon while (note. Melissa){ do nothing } if (no. Milk){ buy milk; } remove note. Landon leave note. Melissa if (no note. Landon){ if (no. Milk){ buy milk; } } remove note. Melissa

Too much milk “w/o waiting”? lock () if (no. Note && no. Milk){ leave Too much milk “w/o waiting”? lock () if (no. Note && no. Milk){ leave note “at store” Not holding unlock () buy milk lock () remove note unlock () } else { unlock () } lock () if (no. Note && no. Milk){ leave note “at store” unlock () buy milk lock () remove note unlock () } else { unlock () } Only hold lock while handling shared resource.

Review of invariants ê What is an invariant? ê A “consistent/sane state” ê Something Review of invariants ê What is an invariant? ê A “consistent/sane state” ê Something that is “always” true ê When can an invariant be broken? ê Can only be broken while lock is held ê And only by thread holding the lock ê Really a “public” invariant ê What is the data’s state in when the lock is free ê Like having a room tidy before guests arrive ê Hold a lock whenever manipulating shared data

More on invariants ê What about reading shared data? ê Still must hold lock More on invariants ê What about reading shared data? ê Still must hold lock ê Else another thread could break invariant ê (Thread A prints Q as Thread B enqueues) ê Hold a lock whenever reasoning about shared data

Intro to ordering constraints ê Say you want dequeue to wait while the queue Intro to ordering constraints ê Say you want dequeue to wait while the queue is empty ê Can we just busy-wait? ê No! ê Still holding lock dequeue () { lock (q. Lock); element=NULL; while (head==NULL) {} // remove head element=head->next; head->next=NULL; unlock (q. Lock); return element; }

Release lock before spinning? What can go wrong? Another dequeuer could “steal” our element Release lock before spinning? What can go wrong? Another dequeuer could “steal” our element Head might be NULL when we try to remove entry dequeue () { lock (q. Lock); element=NULL; unlock (q. Lock); while (head==NULL) {} lock (q. Lock); // remove head element=head->next; head->next=NULL; unlock (q. Lock); return element; }

One more try ê Does it work? ê Seems ok ê Why? ê Sh. One more try ê Does it work? ê Seems ok ê Why? ê Sh. S protected ê What’s wrong? ê Busy-waiting ê Wasteful dequeue () { lock (q. Lock); element=NULL; while (head==NULL) { unlock (q. Lock); } // remove head element=head->next; head->next=NULL; unlock (q. Lock); return element; }

Ideal solution ê Would like dequeueing thread to “sleep” ê Add self to “waiting Ideal solution ê Would like dequeueing thread to “sleep” ê Add self to “waiting list” ê Enqueuer can wake up when Q is non-empty ê Problem: what to do with the lock? ê Why can’t dequeueing thread sleep with lock? ê Enqueuer would never be able to add

Release the lock before sleep? enqueue () { acquire lock find tail of queue Release the lock before sleep? enqueue () { acquire lock find tail of queue add new element if (dequeuer waiting){ remove from wait list wake up dequeuer } release lock } dequeue () { acquire lock … if (queue empty) { release lock add self to wait list sleep } … release lock } Does this work?

Release the lock before sleep? 2 enqueue () { acquire lock find tail of Release the lock before sleep? 2 enqueue () { acquire lock find tail of queue add new element if (dequeuer waiting){ remove from wait list wake up dequeuer } release lock } dequeue () { acquire lock … if (queue empty) { release lock add self to wait list sleep } … release lock } 1 3 Thread can sleep forever Other problems? Wait list is shared and unprotected. (bad idea)

Release the lock before sleep? enqueue () { acquire lock find tail of queue Release the lock before sleep? enqueue () { acquire lock find tail of queue add new element if (dequeuer waiting){ remove from wait list wake up dequeuer } release lock } dequeue () { acquire lock … if (queue empty) { add self to wait list release lock sleep } … release lock }

Release the lock before sleep? 2 enqueue () { acquire lock find tail of Release the lock before sleep? 2 enqueue () { acquire lock find tail of queue add new element if (dequeuer waiting){ remove from wait list wake up dequeuer } release lock } dequeue () { acquire lock … if (queue empty) { add self to wait list release lock sleep } … release lock } 1 3 Problem: missed wake-up Note: this can be fixed, but it’s messy

Two types of synchronization ê As before we need to raise the level of Two types of synchronization ê As before we need to raise the level of abstraction 1. Mutual exclusion ê One thread doing something at a time ê Use locks 2. Ordering constraints ê Describes “before-after” relationships ê One thread waits for another ê Use monitors

Course administration ê Discussion sections ê Everything going smoothly? ê Homework problems ê First Course administration ê Discussion sections ê Everything going smoothly? ê Homework problems ê First set posted on Friday ê Go over in discussion section ê Students will present solutions

Course administration ê Project 0 ê 6/10 have perfect scores ê If you are Course administration ê Project 0 ê 6/10 have perfect scores ê If you are struggling with P 0, think about why ê ê ê C++ language/Linux environment issues? Coding before thinking? Not reading the spec closely enough? Time management (i. e. not starting early enough)? Group management? ê Fix any non-language issues before Project 1

Course administration ê Project 1 is out today (due February 21 st) ê Project Course administration ê Project 1 is out today (due February 21 st) ê Project 1 data structures ê STL (less coding , more learning) ê Roll-your-own (less learning, more coding) ê Either is acceptable ê Reminder: office hours (come talk to us!) ê ê Me: 1 -3 on Wednesdays in D 304 Amre: 11: 00 – 1: 00 on Tuesdays in North Bldg N 001 Quinn: 12: 00 – 2: 00 on Mondays in Teer cluster Extra hours near project deadline ê Other questions or concerns?

Monitor synchronization 1. Mutual exclusion ê One thread doing something at a time ê Monitor synchronization 1. Mutual exclusion ê One thread doing something at a time ê Use locks 2. Ordering constraints ê Describes “before-after” relationships ê One thread waits for another ê Monitor: a lock + its condition variable

Locks and condition variables ê Condition variables ê Lets threads sleep inside a critical Locks and condition variables ê Condition variables ê Lets threads sleep inside a critical section ê Internal atomic actions (for now, by definition) // begin atomic release lock put thread on wait queue go to sleep // end atomic ê CV State = queue of waiting threads + one lock

Condition variable operations Lock always held wait (){ release lock put thread on wait Condition variable operations Lock always held wait (){ release lock put thread on wait queue go to sleep // after wake up acquire lock } Atomic Lock usually held signal (){ wakeup one waiter (if any) } Atomic Lock usually held broadcast (){ wakeup all waiters (if any) } Atomic Lock always held

CVs and invariants ê User programs ê Ok to leave invariants violated before wait? CVs and invariants ê User programs ê Ok to leave invariants violated before wait? ê No: wait can release the lock ê Larger rule about returning from wait ê Lock may have changed hands ê State can change between wait entry and return ê Don’t make assumptions about shared state

Multi-threaded queue enqueue () { acquire lock dequeue () { acquire lock find tail Multi-threaded queue enqueue () { acquire lock dequeue () { acquire lock find tail of queue add new element if (queue empty) { wait (lock, CV) } signal (lock, CV) remove item from queue release lock return removed item release lock } } What if “queue empty” takes more than one instruction? Any problems with the “if” statement in dequeue?

Multi-threaded queue enqueue () { acquire lock dequeue () { acquire lock find tail Multi-threaded queue enqueue () { acquire lock dequeue () { acquire lock find tail of queue add new element if (queue empty) { // begin atomic wait release lock sleep and wait // end atomic wait re-acquire lock signal (lock, CV) release lock } } remove item from queue release lock return removed item }

Multi-threaded queue enqueue () { acquire lock 2 find tail of queue add new Multi-threaded queue enqueue () { acquire lock 2 find tail of queue add new element signal (lock, CV) release lock } dequeue () { acquire lock … return removed item } 3 dequeue () { acquire lock 1 4 if (queue empty) { // begin atomic wait release lock sleep and wait // end atomic wait re-acquire lock } remove item from queue release lock return removed item }

Multi-threaded queue enqueue () { acquire lock dequeue () { acquire lock find tail Multi-threaded queue enqueue () { acquire lock dequeue () { acquire lock find tail of queue add new element if (queue empty) { // begin atomic wait release lock sleep and wait // end atomic wait re-acquire lock signal (lock, CV) release lock } How to solve? } remove item from queue release lock return removed item }

Multi-threaded queue enqueue () { acquire lock The “condition” in condition variable dequeue () Multi-threaded queue enqueue () { acquire lock The “condition” in condition variable dequeue () { acquire lock find tail of queue add new element while (queue empty) { wait (lock, CV) } signal (lock, CV) remove item from queue release lock return removed item release lock } } Solve with a while loop (“loop before you leap”) You can now do the P 1 disk scheduler …

Mesa vs. Hoare monitors ê So far, we’ve described Mesa monitors ê After waking Mesa vs. Hoare monitors ê So far, we’ve described Mesa monitors ê After waking up, no special priority ê Threads have to recheck condition ê Hoare semantics are “simpler” ê But complicate implementation

Hoare semantics ê Condition guaranteed true after wakeup ê i. e. no need to Hoare semantics ê Condition guaranteed true after wakeup ê i. e. no need to loop ê Waiter acquires lock before any threads run ê (since lock protects condition) ê Including the signaler! ê Signaler must give up lock and signal atomically ê What would this mean for invariants? ê All invariants must be established before signal ê We will use Mesa semantics

Tips for using monitors 1. 2. List the shared data needed for problem Figure Tips for using monitors 1. 2. List the shared data needed for problem Figure out the locks ê 1 lock per group of shared data 3. 4. Bracket code that uses shared data with lock/unlock Think about before-after conditions ê ê ê 1 condition variable per type of condition CV’s lock should protect data used to evaluate condition Call wait when you need to wait on a condition Loop to re-check condition when wait returns Call signal when condition changes Ensure invariants are established when lock is not held (unlock, wait)

Producer-consumer problem ê Producer makes something consumer wants ê Goal: avoid lock-step (direct hand-off) Producer-consumer problem ê Producer makes something consumer wants ê Goal: avoid lock-step (direct hand-off) Soda drinker (consumer) Delivery person (producer) Problem: everyone’s time is wasted.

Producer-consumer problem ê Instead, use a fixed-size, shared buffer ê Producer puts in (must Producer-consumer problem ê Instead, use a fixed-size, shared buffer ê Producer puts in (must wait when full) ê Consumer takes out (must wait when empty) ê Must synchronize access to buffer ê Examples ê Unix pipes: cpp | cc 1 | cc 2 | as ê Interaction between hardware devices/buffers ê Project 1 disk scheduler

Use a soda machine as a buffer Delivery person (producer) Soda drinker (consumer) Vending Use a soda machine as a buffer Delivery person (producer) Soda drinker (consumer) Vending machine (buffer)

Solving producer-consumer 1. What are the variables/shared state? ê Soda machine buffer ê Number Solving producer-consumer 1. What are the variables/shared state? ê Soda machine buffer ê Number of sodas in machine (≤ Max. Sodas) 2. Locks? ê 1 to protect all shared state (soda. Lock) 3. Mutual exclusion? ê Only one thread can manipulate machine at a time 4. Ordering constraints? ê Consumer must wait if machine is empty (CV has. Soda) ê Producer must wait if machine is full (CV has. Room)

Producer-consumer code consumer () { lock (soda. Lock) producer () { lock (soda. Lock) Producer-consumer code consumer () { lock (soda. Lock) producer () { lock (soda. Lock) while (num. Sodas == 0) { wait (soda. Lock, has. Soda) } take soda out of machine add soda to machine signal (has. Room) signal (has. Soda) unlock (soda. Lock) } while(num. Sodas==Max. Sodas){ wait (soda. Lock, has. Room) } unlock (soda. Lock) }

Variations: looping producer ê Producer ê Infinite loop ok? ê Why/why not? producer () Variations: looping producer ê Producer ê Infinite loop ok? ê Why/why not? producer () { lock (soda. Lock) while (1) { while(num. Sodas==Max. Sodas){ wait (soda. Lock, has. Room) } ê Release lock in wait call add soda to machine signal (has. Soda) } unlock (soda. Lock) }

Variations: resting producer ê Producer ê Sleep ok? ê Why/why not? ê Shouldn’t hold Variations: resting producer ê Producer ê Sleep ok? ê Why/why not? ê Shouldn’t hold locks during a slow operation producer () { lock (soda. Lock) while (1) { sleep (1 hour) while(num. Sodas==Max. Sodas){ wait (soda. Lock, has. Room) } add soda to machine signal (has. Soda) } unlock (soda. Lock) }

Variations: one CV? consumer () { lock (soda. Lock) producer () { lock (soda. Variations: one CV? consumer () { lock (soda. Lock) producer () { lock (soda. Lock) while (num. Sodas == 0) { wait (soda. Lock, has. Ror. S) } take soda out of machine add soda to machine signal (has. Ror. S) unlock (soda. Lock) } while(num. Sodas==Max. Sodas){ wait (soda. Lock, has. Ror. S) } unlock (soda. Lock) } Two producers, two consumers: who consumes a signal? Producer. A and Consumer. B wait while Consumer. C signals?

Variations: one CV? consumer () { lock (soda. Lock) producer () { lock (soda. Variations: one CV? consumer () { lock (soda. Lock) producer () { lock (soda. Lock) while (num. Sodas == 0) { wait (soda. Lock, has. Ror. S) } take soda out of machine add soda to machine signal (has. Ror. S) unlock (soda. Lock) } while(num. Sodas==Max. Sodas){ wait (soda. Lock, has. Ror. S) } unlock (soda. Lock) } Is it possible to have a producer and consumer both waiting? max=1, c. A and c. B wait, p. C adds/signals, p. D waits, c. A wakes

Variations: one CV? consumer () { lock (soda. Lock) producer () { lock (soda. Variations: one CV? consumer () { lock (soda. Lock) producer () { lock (soda. Lock) while (num. Sodas == 0) { wait (soda. Lock, has. Ror. S) } take soda out of machine add soda to machine signal (has. Ror. S) unlock (soda. Lock) } while(num. Sodas==Max. Sodas){ wait (soda. Lock, has. Ror. S) } unlock (soda. Lock) } How can we make the one CV solution work?

Variations: one CV? consumer () { lock (soda. Lock) producer () { lock (soda. Variations: one CV? consumer () { lock (soda. Lock) producer () { lock (soda. Lock) while (num. Sodas == 0) { wait (soda. Lock, has. Ror. S) } take soda out of machine add soda to machine broadcast (has. Ror. S) unlock (soda. Lock) } while(num. Sodas==Max. Sodas){ wait (soda. Lock, has. Ror. S) } unlock (soda. Lock) } Use broadcast instead of signal

Broadcast vs signal ê Can I always use broadcast instead of signal? ê Yes, Broadcast vs signal ê Can I always use broadcast instead of signal? ê Yes, assuming threads recheck condition ê Why might I use signal instead? ê Efficiency (spurious wakeups) ê May wakeup threads for no good reason ê Next class: reader/writer locks