Title: Review
1Review
- The Critical Section problem
- Petersons Algorithm
- Implementing Atomicity
- Busy waiting
- Blocking
2Outline
- Semaphores
- what they are
- how they are used
- Monitors
- what they are
- how they are used
3Whats wrong with this picture?
- Our solutions to CS have two unappealing
features - They are mistifying and unclear
- They use busy waiting
4Semaphores
- A semaphore consists of
- A variable s
- A thread set T
- A function P (Passeren wait)
- A function V (Vrijgeven signal)
- syntax example Semaphore s(5)
5Semaphore Operations
- Initialization(val)
-
- s val
- T
-
6P(s)
- s--
- if(s lt 0)
-
- add caller thread to T
- block
-
- This code executes atomically.
7V(s)
- s
- if(s ? 0)
-
- Remove a thread t from T
- Unblock(t)
-
- This code executes atomically.
8Mutual exclusion with semaphores
- Thread T0
- while (!terminate)
- ltentry0gt
- CS
- ltexit0gt
- NCS0
-
- Thread T1
- while (!terminate)
- ltentry1gt
- CS
- ltexit1gt
- NCS1
-
init s 1
9Questions, Questions...
- What if we have n threads instead of 2?
- What if we want to allow at most k threads in the
CS?
10Binary Semaphore
- Two types
- Counting semaphores
- s takes any values
- Binary semaphores
- s takes only 0 or 1
11Binary Semaphores
- P(s)
- if (s 0)
-
- Add caller thread to T
- Block
-
- else
- s--
V(s) if (T ! ?) Remove a thread t from
T Unblock(t) else if (s 0) s 1
12Important properties of Semaphores -I
- Semaphores are non-negative integers
- The only operations you can use to change the
value of a semaphore are P() and V() (except for
the initial setup) - Semaphores have two uses
- Mutual exclusion you know all about it
- Synchronization how do we make sure that T1
completes execution before T2 starts?
13Important Propertiesof Semaphores- II
- We assume that a semaphore is fair
- No thread t that is blocked because of a P(s)
operation remains blocked if s is Vd infinitely
often - Does this guarantee bounded waiting?
- In practice, FIFO is mostly used, transforming
the set into a queue. - V(s) never blocks.
- Binary semaphores are as expressive as general
semaphores (given one can implement the other) - But they are different
- s 0 V(s) V(s) P(s) P(s) VS
- s 0 Vb(s) Vb(s) Pb(s) Pb(s)
14Classic Problems Producer-Consumer I
- Producer adds items to a shared buffer
- Consumer takes them out
- Buffer avoids producers and consumers to proceed
in lock step - Buffer is finite
- Examples
- cppccas
- coke machine
- ready queue in a multithreaded multiprocessor
- .
15Producer Consumer II
- Two types of constraint
- Mutual Exclusion
- Only one thread can manipulate the buffer at any
one time - Condition Synchronization
- Consumer must wait if buffer is empty
- Producer must wait if buffer is full
- Are these safety or liveness properties?
16Producer Consumer III
- Use a separate semaphore for each constraint
- Semaphore mutex // protects the shared buffer
- Semaphore fullSlots // counts full slots if 0,
no // coke - Semaphore emptySlots // counts empty slots
if 0, // nowhere to put more coke
17Producer Consumer IV
- Semaphore new mutex (1)
- Semaphore new emptySlots(numSlots)
- Semaphore new fullSlots(0)
- Producer()
- P(emptySlots)
- P(mutex)
- put 1 coke in the machine
- V(mutex)
- V(fullSlots)
- Consumer()
- P(fullSlots)
- P(mutex)
- take a coke out
- V(mutex)
- V(emptySlots)
Is the order of Ps important?
Is the order of Vs important?
18Beyond Semaphores
- Semaphores huge step forward (immagine having to
do producer consumer with Petersons algorithm) - BUTSemaphores are used both for mutex and
condition synchronization - hard to read code
- hard to get code right
19Monitors (late 60s, early 70s)
- A programming language construct, much like what
we call today objects - Consists of
- General purpose variables
- Methods
- Initialization code (constructor)
- Condition variables
- Can implement a monitor-like style of programming
without without the language construct, using a
combination of locks and condition variables
20Locks
- A lock provides mutual exclusion to shared data
- LockAcquire() wait until lock is free, then
grab it - LockRelease() unlock wake up anyone waiting
in Acquire - Rules
- Always acquire before accessing a shared data
structure - Always release after finishing with shared data
structure - Lock is initially free
21Example Producer Consumer
- addToBuff()
- lock.Acquire() // lock before using shared
data - put item on buffer, if there is space
- lock.Release()
-
- removeFromBuff()
- lock.Acquire() // lock before using shared
data - if something on buffer, remove it
- lock.Release()
- return item
-
22Houston, we have a problem
- How do we change removeFromBuff so that it check
if there is something in the buffer before trying
to remove it? - Logically, want to suspend thread inside the
critical section - What is the problem?
- Solution suspend and atomically release lock
23Condition Variables
- A queue of threads waiting for something inside
the critical section (why does it not violate
safety?) - Condition variables have two operations
- Wait()
- Calling thread blocks, releases lock (gives up
monitor) - Wait on a queue until someone signals variable
- Signal()
- Unblock someone who was waiting on the variable
- Broadcast()
- Unblock all waiting on the variable
24Producer Consumer, again
- addToBuff()
- lock.Acquire() // lock before using shared
data - put item on buffer
- condition.signal()
- lock.Release()
-
- removeFromBuff()
- lock.Acquire() // lock before using shared
data - while nothing on buffer
- condition.wait(lock)
-
- remove item from buffer
- lock.Release()
- return item
25A dilemma
- What happens to a thread that signals on a
condition variable while in the middle of a
critical section?
26Hoare Monitors
- Assume thread Q waiting on condition x
- Assume thread P is in the monitor
- Assume thread P calls x.signal
- P gives up monitor, P blocks!
- Q takes over monitor, runs
- Q finishes, gives up monitor
- P takes over monitor, resumes
27Example Hoare Monitors
- fn1()
-
- x.wait // T1 blocks
- // T1 resumes
- // T1 finishes
- fn4()
-
- x.signal // T2 blocks
- // T2 resumes
28Per Brinch Hansens Monitors
- Assume thread Q waiting on condition x
- Assume thread P is in the monitor
- Assume thread P calls x.signal
- P continues, finishes
- Q takes over monitor, runs
- Q finishes, gives up monitor
29Example Hansen Monitors
- fn1()
-
- x.wait // T1 blocks
- // T1 resumes
- // T1 finishes
- fn4()
-
- x.signal // T2 continues
- // T2 finishes
-
30Tradeoff
- Hoare
- Awkward in programming
- Cleaner, good for mathematical proofs
- Main advantage when a condition variable is
signalled, condition does not change - Used bymost textbooks
- Hansen
- Easier to program
- Can lead to synchronization bugs
- Used by most systems
31Classic Problems 2Readers-Writers
- Motivation
- shared database (bank, seat reservation system)
- Two classes of users
- readers (never modify database)
- writers (read and modify database)
- Want to maximize concurrency
- at most one writer in database
- if no writer, any number of readers in database
32Towards a solution
- State variables
- ar --- active readers (readers in CS)
- aw --- active writers (writers in CS)
- Condition variables
- oktoread (readers can enter critical section)
- oktowrite (writer can enter critical section)
- Safety
- ar 0 ? aw 0 ? (aw 0 ? (aw 1 ? ar 0))
33Structure of the solution
- Databaseread()
- wait until no writers
- access database
- if no readers in database, let writer in
- Databasewrite()
- wait until no readers or writers
- access database
- wake up waiting readers and writers
Use procedures startRead, doneRead, startWrite,
doneWrite
34The procedures
- DatabasestartRead()
- lock.Acquire()
- while (aw)
- okToRead.Wait(lock)
-
- ar ar1
- okToRead.signal
- lock.Release()
-
- DatabasedoneRead()
- lock.Acquire()
- ar ar 1
- if (ar 0) okToWrite.signal
- lock.Release()
- DatabasestartWrite()
- lock.Acquire()
- while (aw ? ar)
- okToWrite.Wait(lock)
-
- aw 1
- lock.Release()
-
- DatabasedoneWrite()
- lock.Acquire()
- aw 0
- okToRead.signal
- okToWrite.signal
- lock.Release()
35Semaphores and Monitors - I
- Can we build monitors using semaphores?
- Try 1
- Wait() P(s) Signal() V(s)
- Try 2
- Wait(Lock lock) Signal()
- lock.Release() V(s)
- P(s)
- lock.Acquire()
36Semaphores and Monitors-II
- Try 3
- Signal()
- if semaphore queue is not empty V(s)
-
- REMEMBER
- If a thread signals on a condition on which no
thread is waiting, the result is a no-op. - If a thread executes a V on a semaphore on which
no thread is waiting, the semaphore is
incremented!
37A few matters of style
- Always do things the same way
- Always use monitors (condition variables plus
locks) - Always hold lock when operating on a condition
variable - Always grab lock at the beginning of a procedure
and release it before return - Always use while (not if) to check for the value
of a condition variable - (Almost) never use sleep to synchronize
38Example Java
- Each object has a monitor
- User can specify the keyword synchronized in
front of methods that must execute with mutual
exclusion - Use Hansen monitors
- No individually named condition variables (just
one anonymous condition variable) - wait()
- notify()
- notifyAll()