You are on page 1of 26

Operating Systems

W7 - Locks

Le Hai Duong, PhD. (lhduong@hcmiu.edu.vn)


Outline
● Basic idea
● Pthread locks
● Building a lock
● Evaluating locks
● Solution 1: controlling interrupts
● Solution 2: just use loads/stores
● Spin locks
○ Solution 3: test-and-set
○ Solution 4: compare-and-set
○ Solution 5: load-linked and store-conditional
○ Solution 6: fetch-and-add
● Solution 7: yield() → deschedule itself
● Solution 8: using queue and sleeping
● Linux kernel’s implementation
● want to execute a series of instructions atomically ←
disturbed by interrupts
● solution → put locks around critical sections
→ any critical section executes as if it were a
single atomic instruction
Basic idea

● lock variable → mutex


○ available/unlocked/free
○ acquire/locked/held
Pthread locks

● POSIX library provides lock called mutex for mutual


exclusion between threads
Counter with lock
...
int max;
volatile int counter = 0; // shared global variable
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

void *mythread(void *arg) {


char *letter = arg;
int i; // stack (private per thread)
printf("%s: begin [addr of i: %p]\n", letter, &i);
for (i = 0; i < max; i++) {
pthread_mutex_lock(&m);
counter = counter + 1; // shared: only one
pthread_mutex_unlock(&m);
}
printf("%s: done\n", letter);
return NULL;
}
Counter with lock (result)
Building a lock

● How can we build an efficient lock?


● What hardware/OS support needed?
Evaluating locks

1. correctness: does the lock preventing multiple threads


from entering a critical section?
2. fairness: each thread contending for the lock get a fair
shot at acquiring it
3. starve: one/many threads never obtain the lock
4. performance: time overheads added by using the lock
Solution 1: Controlling interrupts

● problems
○ (1) a thread can monopolize the processor by
keeping the lock → ⛔ correctness/fairness/starve
○ (2) not work on multiprocessors
○ (3) interrupt losts
○ (4) inefficient → ⛔ performance
Solution 2: A failed attempt
Just using loads/stores
● spin-wait for the lock
● problems
○ ⛔ correctness/fairness/starve
○ ⛔ performance

both threads set flag to 1 and → need hardware support → new


enter the critical section instruction
Solution 3: Building worki spin locks
with test-and-set

● hardware support → test-and-set (or atomic exchange) instruction


● require a preemptive scheduler
Evaluating spin locks

● (1) correctness ✅
● (2) fairness ⛔
● (3) starvation ⛔
● (4) performance ⛔
○ threads will spin for the duration of a time slice before gigging up the
CPU → waste of CPU cycles
Solution 4: Compare-and-swap
Solution 5: Load-linked and
store-conditional

● store-conditional only succeeds if no intervening store to the


address has taken place
Solution 6: Fetch-and-add
● use a ticket and turn variables in combination to build a lock
● solve the starvation problem
Too much spinning: what now?

● spin locks are quite inefficient


● N threads → N - 1 time slices may be wasted
→ need OS support
Solution 7: A simple approach: yield()
● yield() → give up the CPU and let another thread run →
deschedules itself
● problems
○ run-and-yield for big number of threads → costly
○ starvation problem
Solution 8: using queues → sleeping
instead of spinning

put a thread to sleep


wake a particular thread up

● more efficient lock compared to spin lock


● solve the starvation problem
● race condition before the call to park() → go back to sleep after
had been woken up → wakeup/waiting race
○ Solaris solves by adding setpark() → interrupt calls unpark
before park is actually called, the subsequence park returns
immediately instead of sleeping
Linux kernel’s implementation
● hybrid
○ spin for 1 round
○ queue with sleeping

You might also like