Title: Operating Systems CMPSCI 377 Advanced Synchronization
1Operating SystemsCMPSCI 377Advanced
Synchronization
- Emery Berger and Mark Corner
- University of Massachusetts Amherst
2Why Synchronization?
- Synchronization serves two purposes
- Ensure safety for shared updates
- Avoid race conditions
- Coordinate actions of threads
- Parallel computation
- Event notification
- ALL interleavings must be correct
- there are lots of interleavings of events
- also constrain as little as possible
3Synch. Operations
- Safety
- Locks provide mutual exclusion
- Coordination
- Condition variables provide ordering
4Safety
- Multiple threads/processes
- access shared resource simultaneously
- Safe only if
- All accesses have no effect on resource,e.g.,
reading a variable, or - All accesses idempotent
- E.g., a abs(x), a highbit(a)
- Only one access at a time mutual exclusion
5Safety Example
- The too much milk problem
- Model of need to synchronize activities
6Why You Need Locks
- thread A
- if (no milk no note)
- leave note
- buy milk
- remove note
thread B if (no milk no note) leave note
buy milk remove note
7Mutual Exclusion
- Prevent more than one thread from accessing
critical section - Serializes access to section
- Lock, update, unlock
- lock (l)
- update data / critical section /
- unlock (l)
8Too Much Milk Locks
thread A lock(l) if (no milk) buy
milk unlock(l)
thread B lock(l) if (no milk) buy
milk unlock(l)
9What data is shared?
- Some data is shared
- code, data, heap
- Each thread has private data
- Stack, SP, PC
- All access to shared data must be safe
10Exercise!
- Simple multi-threaded program
- N number of iterations
- Spawn that many threads to compute
- value expensiveComputation(i)
- Add value (safely!) to total
- Use
- pthread_mutex_init, _lock, _unlock
- pthread_mutex_t myLock
- pthread_create, pthread_join
- pthread_t threadsN
11Prototypes
- pthread_t theseArethreads100
- pthread_mutex_t thisIsALock
- typedef void fnType (void )
- pthread_create (pthread_t , fnType, void )
- pthread_join (pthread_t /, void /)
- pthread_mutex_init (pthread_mutex_t )
- pthread_mutex_lock (pthread_mutex_t )
- pthread_mutex_unlock (pthread_mutex_t )
12Solution
int main (int argc, char argv) int n
atoi(argv1) // mutex init
pthread_mutex_init (lock) // allocate
threads pthread_t threads new pthread_tn
// spawn threads for (int i 0 iltn i)
// heap allocate args int newI new
int newI i pthread_create
(threadsi, NULL, wrapper, (void ) newI)
// join for (int i 0 iltn i)
pthread_join (threadsi, NULL) // done
printf (total d\n, total) return 0
- include ltpthread.hgt
- int total 0
- pthread_mutex_t lock
- void wrapper (void x)
- int v ((int ) x)
- delete ((int ) x)
- int res expComp (v)
- pthread_mutex_lock (lock)
- total res
- pthread_mutex_unlock (lock)
- return NULL
-
13Synch. Operations
- Safety
- Locks provide mutual exclusion
- Coordination
- Condition variables provide ordering
13
14Synch Problem Queue
- Suppose we have a thread-safe queue
- insert(item), remove(), empty()
- must protect access with locks
- Options for remove when queue empty
- Return special error value (e.g., NULL)
- Throw an exception
- Wait for something to appear in the queue
15Three Possible Solutions
- Spin
- Works?
- Could release lock
- Works?
- Re acquire Lock
lock() while(empty()) unlock()
unlock() while(empty()) lock()
lock() while (empty()) unlock() lock() Get
item unlock()
16Solution Sleep!
- Sleep
- dont run me until something happens
- What about this?
- Cannot hold lock while sleeping!
Dequeue() lock() if (queue empty)
sleep() take one item unlock()
Enqueue() lock() insert item if
(thread waiting) wake up dequeuer()
unlock()
17Quick Exercise
Dequeue() lock() if (queue empty)
unlock() sleep() else unlock
Enqueue() lock() insert item if (thread
waiting) wake up dequeuer() unlock()
- No!
- deq releases lock, then enq looks for sleeping
thread
18Condition Variables
- Make it possible/easy to go to sleep
- Atomically
- release lock
- put thread on wait queue
- go to sleep
- Each cv has a queue of waiting threads
- Worry about threads that have been put on the
wait queue but have NOT gone to sleep yet? - no, because those two actions are atomic
- Each condition variable associated with one lock
19Condition Variables
- Wait for 1 event, atomically release lock
- wait(Lock l, CV c)
- If queue is empty, wait
- Atomically releases lock, goes to sleep
- You must be holding lock!
- May reacquire lock when awakened (pthreads do)
- signal(CV c)
- Insert item in queue
- Wakes up one waiting thread, if any
- broadcast(CV c)
- Wakes up all waiting threads
- Monitors locks condition variables
- Sometimes combined with data structures
20Longer Excercise
- Implement Producer Consumer
- One thread enqueues, another dequeues
Dequeue() lock(A) while (queue empty)
wait(A, C) take item out
unlock(A)
Enqueue() lock(A) insert item signal(C) u
nlock(A)
- Two Questions?
- Can I use if instead of while?
- Can I signal after unlock?
21Longer Exercise Bounded Buffer
- Assume coke machine holds "max" cokes
- Write for unlimited number of threads
- producers/consumers
- Hint need two CVs
producer() lock(cokeLock) while
(numCokes max) wait(cokeLock,
hasRoom) add coke to machine
signal(hasCoke) unlock(cokeLock)
consumer() lock(cokeLock) while
(numCokes 0) wait(cokeLock,
hasCoke) take coke out of machine
signal(hasRoom) unlock(cokeLock)