Locks
Thread Execution
- what can happen when multiple threads read and write shared states?
- time-of-check to time-of-use: shared state might be modified by another thread
- race condition: different scheduling orders can lead to results that are semantically different
- would be much easier to reason if we coordinate/synchronize threads' accesses to shared states
- synchronization primitive: software abstractions that can be used to synchronize threads' actions
Locks Basics
- a synchronization primitive that guarantees exclusive access (mutual exclusion) to a designated section code (critical section)
- APIs
lock_acquire
: acquires a lock, does not return until lock is acquired
lock_release
: releases a lock, available for other threads to acquire
- the programmer's responsibility to acquire the lock before accessing a shared resource and release it after
- lock properties
- safety: only one thread in the critical section at a time
- progress (a liveness property): a thread can enter the critical section if no one's there
- bounded wait (a fairness property): there's an upperbound to the wait, can't keep skipping over a thread
- how do we implement a lock?
struct lock { bool locked; };
lock_acquire(struct lock *lk) {
while (lk->locked) { ; } // lk->locked == false when the loop exits
lk->locked = true;
}
lock_release(struct lock* lk) {
lk->locked = false;
}
- does this implementation satisfy properties of a lock?
- note that lock itself is a shared state!
need hardware support to atomically perform the read and modify when lock is free!
- test and set instruction
- sets the value to 1 if value is 0, returns the old value
- otherwise just returns the value read
atomic instruction attempt
lock_acquire(struct lock *lk) {
while ( test_and_set(&lk->locked) ) { ; }
// lk->locked was set from false to true by the current thread when the loop exits
}
and now we have a lock!
Types of Lock
- spinlock
- when the lock is not free, keep checking (spinning on the CPU) until it's free
- when is this a desired property and when is it not?
- sleeplock/mutex
- when the lock is not free, blocks/sleeps until it's free
- when is this a desired property and when is it not?
- which type of lock did we just implement?
- how would we implement the other type?