1 / 62

CENG334 Introduction to Operating Systems

Learn about monitors, condition variables, and semaphore issues in operating systems, with examples and practical differences in monitor semantics. Covers Hoare vs. Mesa Monitor Semantics in detail.

brosius
Download Presentation

CENG334 Introduction to Operating Systems

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Erol Sahin Dept of Computer Eng. Middle East Technical University Ankara, TURKEY URL: http://kovan.ceng.metu.edu.tr/ceng334 CENG334Introduction to Operating Systems Monitors, Condition variabless Topics: • Monitors • Condition Variables

  2. Adapted from Matt Welsh’s (Harvard University) slides. Issues with Semaphores • Much of the power of semaphores derives from calls todown() and up() that are unmatched • See previous example! • Unlike locks, acquire() and release() are not always paired. • This means it is a lot easier to get into trouble with semaphores. • “More rope” • Would be nice if we had some clean, well-defined language support for synchronization... • Java does!

  3. Monitors • A monitor is an object intended to be used safely by more than one thread. • The defining characteristic of a monitor is that its methods are executed with mutual exclusion. • That is, at each point in time, at most one thread may be executing any of its methods. • also provide Condition Variables (CVs) for threads to temporarily give up exclusive access, in order to wait for some condition to be met, • before regaining exclusive access and resuming their task. • Use CVs for signaling other threads that such conditions have been met.

  4. Condition Variables • Conceptually a condition variable (CV) is a queue of threads, associated with a monitor, upon which a thread may wait for some assertion to become true. • Threads can use CV’s • to temporarily give up exclusive access, in order to wait for some condition to be met, • before regaining exclusive access and resuming their task. • for signaling other threads that such conditions have been met.

  5. Adapted from Matt Welsh’s (Harvard University) slides. Monitors • This style of using locks and CV's to protect access to a sharedobject is often called a monitor • Think of a monitor as a lock protecting an object, plus a queue of waiting threads. Shared data Waiting threads At most one thread in the monitor at a time Methods accessing shared data How is this different than a lock???

  6. Adapted from Matt Welsh’s (Harvard University) slides. Monitors unlocked Shared data Methods accessing shared data

  7. zzzz... Adapted from Matt Welsh’s (Harvard University) slides. Monitors locked Shared data zzzz... Methods accessing shared data Sleeping thread no longer “in” the monitor. (But not on the waiting queue either! Why?)

  8. Adapted from Matt Welsh’s (Harvard University) slides. Monitors Monitor stays locked! (Lock now owned by different thread...) locked Shared data notify() Methods accessing shared data zzzz...

  9. Adapted from Matt Welsh’s (Harvard University) slides. Monitors locked Shared data notify() Methods accessing shared data

  10. Adapted from Matt Welsh’s (Harvard University) slides. Monitors locked Shared data Methods accessing shared data No guarantee which order threads get into the monitor. (Not necessarily FIFO!)

  11. Bank Example monitor Bank{ int TL = 1000; • condition haveTL; void withdraw(int amount) { if (amount < TL) wait(haveTL); TL -= amount; } void deposit(int amount) { TL += amount; notify(haveTL) } }

  12. Bank Example monitor Bank{ int TL = 1000; • condition haveTL; void withdraw(int amount) { while (amount > TL) wait(haveTL); TL -= amount; } void deposit(int amount) { TL += amount; notifyAll(haveTL) } }

  13. Adapted from Matt Welsh’s (Harvard University) slides. Hoare vs. Mesa Monitor Semantics • The monitor notify() operation can have two different meanings: • Hoare monitors (1974) • notify(CV) means to run the waiting thread immediately • Causes notifying thread to block • Mesa monitors (Xerox PARC, 1980) • notify(CV) puts waiting thread back onto the “ready queue” for the monitor • But, notifying thread keeps running

  14. Adapted from Matt Welsh’s (Harvard University) slides. Hoare vs. Mesa Monitor Semantics • The monitor notify() operation can have two different meanings: • Hoare monitors (1974) • notify(CV) means to run the waiting thread immediately • Causes notifying thread to block • Mesa monitors (Xerox PARC, 1980) • notify(CV) puts waiting thread back onto the “ready queue” for the monitor • But, notifying thread keeps running • What's the practical difference? • In Hoare-style semantics, the “condition” that triggered the notify() will always be true when the awoken thread runs • For example, that the buffer is now no longer empty • In Mesa-style semantics, awoken thread has to recheck the condition • Since another thread might have beaten it to the punch

  15. Hoare Monitor Semantics • Hoare monitors (1974) • notify(CV) means to run the waiting thread immediately • Causes notifying thread to block • The signaling thread must wait outside the monitor (at least) until the signaled thread relinquishes occupancy of the monitor by either returning or by again waiting on a condition.

  16. Mesa Monitor Semantics • Mesa monitors (Xerox PARC, 1980) • notify(CV) puts waiting thread back onto the “ready queue” for the monitor • But, notifying thread keeps running • Signaling does not cause the signaling thread to lose occupancy of the monitor. Instead the signaled threads are moved to the e queue.

  17. Hoare vs. Mesa monitors • Need to be careful about precise definition of signal and wait. while (n==0) { wait(not_empty); // If nothing, sleep } item = getItemFromArray(); // Get next item • Why didn’t we do this? • if (n==0) { wait(not_empty); // If nothing, sleep } removeItemFromArray(val); // Get next item • Answer: depends on the type of scheduling • Hoare-style (most textbooks): • Signaler gives lock, CPU to waiter; waiter runs immediately • Waiter gives up lock, processor back to signaler when it exits critical section or if it waits again • Mesa-style (Java, most real operating systems): • Signaler keeps lock and processor • Waiter placed on ready queue with no special priority • Practically, need to check condition again after wait

  18. Revisit: Readers/Writers Problem • Correctness Constraints: • Readers can access database when no writers • Writers can access database when no readers or writers • Only one thread manipulates state variables at a time • State variables (Protected by a lock called “lock”): • int NReaders: Number of active readers; initially = 0 • int WaitingReaders: Number of waiting readers; initially = 0 • int NWriters: Number of active writers; initially = 0 • int WaitingWriters: Number of waiting writers; initially = 0 • Condition canRead = NIL • Conditioin canWrite = NIL

  19. Readers and Writers Monitor ReadersNWriters { int WaitingWriters, WaitingReaders,NReaders, NWriters; Condition CanRead, CanWrite; Void BeginWrite() { if(NWriters == 1 || NReaders > 0) { ++WaitingWriters; wait(CanWrite); --WaitingWriters; } NWriters = 1; } Void EndWrite() { NWriters = 0; if(WaitingReaders) Signal(CanRead); else Signal(CanWrite); } Void BeginRead() { if(NWriters == 1 || WaitingWriters > 0) { ++WaitingReaders; Wait(CanRead); --WaitingReaders; } ++NReaders; Signal(CanRead); } Void EndRead() { if(--NReaders == 0) Signal(CanWrite); }

  20. Readers and Writers Monitor ReadersNWriters { int WaitingWriters, WaitingReaders,NReaders, NWriters; Condition CanRead, CanWrite; Void BeginWrite() { if(NWriters == 1 || NReaders > 0) { ++WaitingWriters; wait(CanWrite); --WaitingWriters; } NWriters = 1; } Void EndWrite() { NWriters = 0; if(WaitingReaders) Signal(CanRead); else Signal(CanWrite); } Void BeginRead() { if(NWriters == 1 || WaitingWriters > 0) { ++WaitingReaders; Wait(CanRead); --WaitingReaders; } ++NReaders; Signal(CanRead); } Void EndRead() { if(--NReaders == 0) Signal(CanWrite); }

  21. Readers and Writers Monitor ReadersNWriters { int WaitingWriters, WaitingReaders,NReaders, NWriters; Condition CanRead, CanWrite; Void BeginWrite() { if(NWriters == 1 || NReaders > 0) { ++WaitingWriters; wait(CanWrite); --WaitingWriters; } NWriters = 1; } Void EndWrite() { NWriters = 0; if(WaitingReaders) notify(CanRead); else notify(CanWrite); } Void BeginRead() { if(NWriters == 1 || WaitingWriters > 0) { ++WaitingReaders; Wait(CanRead); --WaitingReaders; } ++NReaders; Signal(CanRead); } Void EndRead() { if(--NReaders == 0) notify(CanWrite); }

  22. Readers and Writers Monitor ReadersNWriters { int WaitingWriters, WaitingReaders,NReaders, NWriters; Condition CanRead, CanWrite; Void BeginWrite() { if(NWriters == 1 || NReaders > 0) { ++WaitingWriters; wait(CanWrite); --WaitingWriters; } NWriters = 1; } Void EndWrite() { NWriters = 0; if(WaitingReaders) notify(CanRead); else notify(CanWrite); } Void BeginRead() { if(NWriters == 1 || WaitingWriters > 0) { ++WaitingReaders; Wait(CanRead); --WaitingReaders; } ++NReaders; notify(CanRead); } Void EndRead() { if(--NReaders == 0) notify(CanWrite); }

  23. Understanding the Solution • A writer can enter if there are no other active writers and no readers are waiting

  24. Readers and Writers Monitor ReadersNWriters { int WaitingWriters, WaitingReaders,NReaders, NWriters; Condition CanRead, CanWrite; Void BeginWrite() { if(NWriters == 1 || NReaders > 0) { ++WaitingWriters; wait(CanWrite); --WaitingWriters; } NWriters = 1; } Void EndWrite() { NWriters = 0; if(WaitingReaders) notify(CanRead); else notify(CanWrite); } Void BeginRead() { if(NWriters == 1 || WaitingWriters > 0) { ++WaitingReaders; Wait(CanRead); --WaitingReaders; } ++NReaders; notify(CanRead); } Void EndRead() { if(--NReaders == 0) notify(CanWrite); }

  25. Understanding the Solution • A reader can enter if • There are no writers active or waiting • So we can have many readers active all at once • Otherwise, a reader waits (maybe many do)

  26. Readers and Writers Monitor ReadersNWriters { int WaitingWriters, WaitingReaders,NReaders, NWriters; Condition CanRead, CanWrite; Void BeginWrite() { if(NWriters == 1 || NReaders > 0) { ++WaitingWriters; wait(CanWrite); --WaitingWriters; } NWriters = 1; } Void EndWrite() { NWriters = 0; if(WaitingReaders) notify(CanRead); else notify(CanWrite); } Void BeginRead() { if(NWriters == 1 || WaitingWriters > 0) { ++WaitingReaders; Wait(CanRead); --WaitingReaders; } ++NReaders; notify(CanRead); } Void EndRead() { if(--NReaders == 0) notify(CanWrite); }

  27. Understanding the Solution • When a writer finishes, it checks to see if any readers are waiting • If so, it lets one of them enter • That one will let the next one enter, etc… • Similarly, when a reader finishes, if it was the last reader, it lets a writer in (if any is there)

  28. Readers and Writers Monitor ReadersNWriters { int WaitingWriters, WaitingReaders,NReaders, NWriters; Condition CanRead, CanWrite; Void BeginWrite() { if(NWriters == 1 || NReaders > 0) { ++WaitingWriters; wait(CanWrite); --WaitingWriters; } NWriters = 1; } Void EndWrite() { NWriters = 0; if(WaitingReaders) notify(CanRead); else notify(CanWrite); } Void BeginRead() { if(NWriters == 1 || WaitingWriters > 0) { ++WaitingReaders; Wait(CanRead); --WaitingReaders; } ++NReaders; notify(CanRead); } Void EndRead() { if(--NReaders == 0) notify(CanWrite); }

  29. Understanding the Solution • It wants to be fair • If a writer is waiting, readers queue up • If a reader (or another writer) is active or waiting, writers queue up • … this is mostly fair, although once it lets a reader in, it lets ALL waiting readers in all at once, even if some showed up “after” other waiting writers

  30. Adapted from Matt Welsh’s (Harvard University) slides. The Big Picture • The point here is that getting synchronization right is hard • How to pick between locks, semaphores, condvars, monitors??? • Locks are very simple for many cases. • Issues: Maybe not the most efficient solution • For example, can't allow multiple readers but one writer inside a standard lock. • Condition variables allow threads to sleep while holding a lock • Just be sure you understand whether they use Mesa or Hoare semantics! • Semaphores provide pretty general functionality • But also make it really easy to botch things up.

  31. Erol Sahin Dept of Computer Eng. Middle East Technical University Ankara, TURKEY URL: http://kovan.ceng.metu.edu.tr/~erol/Courses/CENG334 CENG334Introduction to Operating Systems Synchronization patterns Topics • Signalling • Rendezvous • Barrier

  32. Signalling • Possibly the simplest use for a semaphore is signaling, which means that one thread sends a signal to another thread to indicate that something has happened. • Signaling makes it possible to guarantee that a section of code in one thread will run before a section of code in another thread; in other words, it solves the serialization problem. Adapted from The Little Book of Semaphores.

  33. Signalling • Imagine that a1 reads a line from a file, and b1 displays the line on the screen. The semaphore in this program guarantees that Thread A has completed a1 before Thread B begins b1. • Here’s how it works: if thread B gets to the wait statement first, it will find the initial value, zero, and it will block. Then when Thread A signals, Thread B proceeds. • Similarly, if Thread A gets to the signal first then the value of the semaphore will be incremented, and when Thread B gets to the wait, it will proceed immediately. • Either way, the order of a1 and b1 is guaranteed. semaphore sem=0; Thread A statement a1; sem.up(); Thread B sem.down(); statement b1; Adapted from The Little Book of Semaphores.

  34. Rendezvous • Generalize the signal pattern so that it works both ways. Thread A has to wait for Thread B and vice versa. In other words, given this code we want to guarantee that a1 happens before b2 and b1 happens before a2. • Your solution should not enforce too many constraints. For example, we don’t care about the order of a1 and b1. In your solution, either order should be possible. • Two threads rendezvous at a point of execution, and neither is allowed to proceed until both have arrived. Thread A statement a1; statement a2; Thread B statement b1; statement b2; Adapted from The Little Book of Semaphores.

  35. Rendezvous - Hint • Generalize the signal pattern so that it works both ways. Thread A has to wait for Thread B and vice versa. In other words, given this code we want to guarantee that a1 happens before b2 and b1 happens before a2. • Your solution should not enforce too many constraints. For example, we don’t care about the order of a1 and b1. In your solution, either order should be possible. • Two threads rendezvous at a point of execution, and neither is allowed to proceed until both have arrived. • Hint: Create two semaphores, named aArrived and bArrived, and initialize them both to zero. aArrived indicates whether Thread A has arrived at the rendezvous, and bArrived likewise. semaphore aArrived=0; semaphore bArrived=0; Thread A statement a1; statement a2; Thread B statement b1; statement b2; Adapted from The Little Book of Semaphores.

  36. Rendezvous - Solution • Generalize the signal pattern so that it works both ways. Thread A has to wait for Thread B and vice versa. In other words, given this code we want to guarantee that a1 happens before b2 and b1 happens before a2. • Your solution should not enforce too many constraints. For example, we don’t care about the order of a1 and b1. In your solution, either order should be possible. • Two threads rendezvous at a point of execution, and neither is allowed to proceed until both have arrived. • Hint: Create two semaphores, named aArrived and bArrived, and initialize them both to zero. aArrived indicates whether Thread A has arrived at the rendezvous, and bArrived likewise. semaphore aArrived=0; semaphore bArrived=0; Thread A statement a1; aArrived.up(); bArrived.down(); statement a2; Thread B statement b1; bArrived.up(); aArrived.down(); statement b2; Adapted from The Little Book of Semaphores.

  37. Rendezvous – A less efficient solution • This solution also works, although it is probably less efficient, since it might have to switch between A and B one time more than necessary. • If A arrives first, it waits for B. When B arrives, it wakes A and might proceed immediately to its wait in which case it blocks, allowing A to reach its signal, after which both threads can proceed.. semaphore aArrived=0; semaphore bArrived=0; Thread A statement a1 bArrived.down()‏ aArrived.up()‏ statement a2 Thread B statement b1; bArrived.up(); aArrived.down(); statement b2; Adapted from The Little Book of Semaphores.

  38. Rendezvous – How about? semaphore aArrived=0; semaphore bArrived=0; Thread A statement a1 bArrived.down()‏ aArrived.up()‏ statement a2 Thread B statement b1; aArrived.down(); bArrived.up(); statement b2; Adapted from The Little Book of Semaphores.

  39. Barrier • Rendezvous solution does not work with more than two threads. • Puzzle: Generalize the rendezvous solution. Every thread should run the following code: rendezvous(); criticalpoint(); The synchronization requirement is that no thread executes critical point until after all threads have executed rendezvous. You can assume that there are n threads and that this value is stored in a variable, n, that is accessible from all threads. When the first n − 1 threads arrive they should block until the nth thread arrives, at which point all the threads may proceed. Adapted from The Little Book of Semaphores.

  40. Barrier - Hint • count keeps track of how many threads have arrived. mutex provides exclusive access to count so that threads can increment it safely. • barrier is locked (zero or negative) until all threads arrive; then it should be unlocked (1 or more). n = thenumberofthreads; count = 0; Semaphore mutex=1, barrier=0; Adapted from The Little Book of Semaphores.

  41. Barrier – Solution? • Since count is protected by a mutex, it counts the number of threads that pass. The first n−1 threads wait when they get to the barrier, which is initially locked. When the nth thread arrives, it unlocks the barrier. • What is wrong with this solution? n = thenumberofthreads; count = 0; Semaphore mutex=1, barrier=0; rendezvous(); mutex.down(); count = count + 1; mutex.up(); if (count == n) barrier.up(); else barrier.down(); Criticalpoint(); Adapted from The Little Book of Semaphores.

  42. Barrier – Solution? • Imagine that n = 5 and that 4 threads are waiting at the barrier. The value of the semaphore is the number of threads in queue, negated, which is -4. • When the 5th thread signals the barrier, one of the waiting threads is allowed to proceed, and the semaphore is incremented to -3. But then no one signals the semaphore again and none of the other threads can pass the barrier. n = thenumberofthreads; count = 0; Semaphore mutex=1, barrier=0; rendezvous(); mutex.down(); count = count + 1; mutex.up(); if (count == n) barrier.up(); else barrier.down(); Criticalpoint(); Adapted from The Little Book of Semaphores.

  43. Barrier – Solution • The only change is another signal after waiting at the barrier. Now as each thread passes, it signals the semaphore so that the next thread can pass. n = thenumberofthreads; count = 0; Semaphore mutex=1, barrier=0; rendezvous(); mutex.down(); count = count + 1; mutex.up(); if (count == n) barrier.up(); else{ barrier.down(); barrier.up(); } Criticalpoint(); Adapted from The Little Book of Semaphores.

  44. Barrier – Bad Solution • Imagine that the first thread enters the mutex and then blocks. Since the mutex is locked, no other threads can enter, so the condition, count==n, will never be true and no one will ever unlock. n = thenumberofthreads; count = 0; Semaphore mutex=1, barrier=0; rendezvous(); mutex.down(); count = count + 1; if (count == n) barrier.up(); barrier.down(); barrier.up(); mutex.up(); Criticalpoint(); Adapted from The Little Book of Semaphores.

  45. Erol Sahin Dept of Computer Eng. Middle East Technical University Ankara, TURKEY URL: http://kovan.ceng.metu.edu.tr/ceng334 CENG334Introduction to Operating Systems Real-world cases Topics: • Race conditions • Priority Inversion

  46. Adapted from Matt Welsh’s (Harvard University) slides. Therac-25 • Computer-controlled radiation therapy machine • In operation between 1983 and 1987, 11 installations

  47. Adapted from Matt Welsh’s (Harvard University) slides. Therac-25 • Capable of delivering electron and photon (X-Ray) treatments • Completely computer controlled • No hardware interlocks to prevent misconfigurations or overdoses! • All software written in PDP-11 assembly language • Cryptic error messages delivered to operator console • “Malfunction 23” • No documentation of these error codes • No indication of which errors are potentially life-threatening • Lots of smoke and mirrors by the manufacturer • Claimed that 10-11 chance of delivering wrong dose to patient • No justification for this claim in the safety analysis documents

  48. Adapted from Matt Welsh’s (Harvard University) slides. Accidents • On several occasions between June '85 and Jan '87 • Massive overdoses to six people • Some of these were lethal • Typical theraputic doses in the 200 rad range • Several overdoses delivered energy of 15,000 – 20,000 rads • Various lawsuits, all settled out of court • Initially, manufacturer claimed that overdoses were impossible

  49. Adapted from Matt Welsh’s (Harvard University) slides. The problem • Therac-25 operator console layout. The lethal computer error occurs when the operator accidentally sets the field (here in red) to "X", notices her mistake, then changes it to "E".

  50. Adapted from Matt Welsh’s (Harvard University) slides. Race Condition #1 • After some trial and error, it was discovered that overdose could be caused by operator editing the dosage on the console too quickly • Operator would enter dosage on console • Move cursor to bottom of screen, then move cursor back up to edit dosage • “Treat” task • Periodically checks “entry done” flag • If flag is set, call subroutine to configure the magnets • Configuring magnets takes about 8 sec • “Magnet” task • Called periodically to check if magnets are ready • Checks if edits have been made to dosage • If so, exits back to calling subroutine to restart the process • Critical bug: Only checks if edits made on the first call! • How this led to overdose: • Operator enters dosage: Triggers magnet setting routine • Operator edits dosage while the magnets are being configured • Magnet routine does not notice edits have been made after first call

More Related