Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chapter 28 Locks Chien-Chung Shen CIS, UD

Similar presentations


Presentation on theme: "Chapter 28 Locks Chien-Chung Shen CIS, UD"— Presentation transcript:

1 Chapter 28 Locks Chien-Chung Shen CIS, UD cshen@cis.udel.edu

2 Basic Ideas Problem on concurrent programming – like to execute a sequence of instructions atomically on single CPU with interrupts Solution: put locks around critical sections lock_t mutex; // some globally-allocated lock ’mutex’ … lock(&mutex); balance = balance + 1; // critlcal section unlock(&mutex);

3 Pthread Locks pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; Pthread_mutex_lock(&lock) ; // wrapper for pthread_mutex_lock() balance = balance + 1; Pthread_mutex_unlock(&lock); Course-grained locking vs. fine-grained locking –“Long” vs. “short” critical sections –example with file access

4 Evaluating Locks Three criteria –Mutual exclusion - correctness –Fairness – avoid starvation –Performance - overhead

5 Controlling Interrupts void lock () { disableInterrupt(); } void unlock() { enable Interrupt(); } Negatives: –Allow calling thread to perform privileged operation –Does not work on multiprocessors

6 First Attempt – use a variable typedef struct __lock_t { int flag; } lock_t; void init(lock_t *mutex) { mutex->flag = 0; // 0 -> lock is available, 1 -> held } void lock(lock_t *mutex) { while (mutex->flag == 1) // TEST the flag ; // spin-wait (do nothing) mutex->flag = 1; // now SET it! } void unlock(lock_t *mutex) { mutex->flag = 0; } What problems does this solution have?

7 Code Interleaving Thread 1 Thread 2 flag == 0 call lock() while (flag == 1) interrupt: switch to Thread 2 call lock() while (flag == 1) flag = 1; interrupt: switch to Thread 1 flag = 1; // set flag to 1 (too!) Problems: –correctness – no guarantee of mutual exclusion –Performance – spin-waiting

8 Test-and-Set Semantics int TestAndSet(int *ptr, int new) { int old = *ptr; // fetch old value at ptr *ptr = new; // store ’new’ into ptr return old; // return the old value } Returns the old value pointed to by ptr, and simultaneously updates said value to new Make “test” (of old lock value) and “set” (of new value) a single atomic operation SPARC – ldstub // load/store unsigned byte x86 – xchg // atomic exchange

9 Spin Lock with Test-and-Set typedef struct __lock_t { int flag; } lock_t; void init(lock_t *mutex) { mutex->flag = 0; // 0 -> lock is available, 1 -> held } void lock(lock_t *mutex) { while (TestAndSet(&lock->flag, 1) == 1) // TEST the flag ; // spin-wait (do nothing) } void unlock(lock_t *mutex) { mutex->flag = 0; } As long as the lock is held by another thread, TestAndSet() will repeatedly return 1, and thus the calling thread will spin-wait What kind of scheduler do we need on single processor? –preemptive scheduler (interrupt threads via timer)

10 Evaluation of Spin Lock Three criteria –Mutual exclusion – correctness yes –Fairness – avoid starvation no –Performance – overhead Bad on single CPU Reasonably well on multiple CPUs, assuming critical sections are short

11 Compare-and-Swap On x86 - compare-and-exchange cmpxchgl Semantics int CompareAndSwap(int *ptr, int expected, int new) { int actual = *ptr; if (actual == expected) *ptr = new; return actual; } Lock void lock(lock_t *lock) { while (CompareAndSwap(&lock->flag, 0, 1) == 1) ; // spin } More powerful than Test-and-Set

12 Ticket Lock with Fetch&Add int FetchAndAdd(int *ptr) { // semantics int old = *ptr; *ptr = old + 1; return old; } typedef struct __lock_t { int ticket; int turn; } lock_t; void lock_init(lock_t *lock) { lock->ticket = 0; lock->turn = 0; } void lock(lock_t *lock) { int myturn = FetchAndAdd(&lock->ticket); // get a ticket while (lock->turn != myturn) ; // spin if not my turn } void unlock(lock_t *lock) { FetchAndAdd(&lock->turn); // enable the next waiting thread } Anything good ? –ensure progress for all threads and fair

13 How to Avoid Spinning ? Need OS support, in addition to hardware void init() { flag = 0; } void lock() { while (TestAndSet(&flag, 1) == 1) // TEST the flag yield(); // give up CPU and move to READY state } void unlock() { flag = 0; } Another overhead ? (think 100 threads) –context switching overhead Still one problem not solved –starvation

14 Sleeping Instead of Spinning Explicitly exert some control over who gets to acquire the lock next after the current holder releases it What “data structure” would you use? –queue park() – put calling thread to sleep unpark() – wake up a thread

15 Queue and Yield/Wakeup typedef struct __lock_t { int flag; int guard; queue_t *q; } lock_t; void lock_init(lock_t *m) { m->flag = 0; m->guard = 0; queue_init(m->q); } void lock(lock_t *m) { while (TestAndSet(&m->guard, 1) == 1) ; //acquire guard lock by spinning if (m->flag == 0) { m->flag = 1; // lock is acquired m->guard = 0; } else { queue_add(m->q, gettid()); // added to the lock’s queue m->guard = 0; park(); // put calling thread to sleep } void unlock(lock_t *m) { while (TestAndSet(&m->guard, 1) == 1) ; //acquire guard lock by spinning if (queue_empty(m->q)) m->flag = 0; // let go of lock; no one wants it else unpark(queue_remove(m->q)); // hold lock (for next thread!) m->guard = 0; }

16 Questions void lock(lock_t *m) { while (TestAndSet(&m->guard, 1) == 1) ; //acquire guard lock by spinning if (m->flag == 0) { m->flag = 1; // lock is acquired m->guard = 0; } else { queue_add(m->q, gettid()); // added to the lock’s queue x: m->guard = 0; y: park(); // put calling thread to sleep } } Why is guard used? Can x and y be swapped ? void unlock(lock_t *m) { while (TestAndSet(&m->guard, 1) == 1) ; //acquire guard lock by spinning if (queue_empty(m->q)) m->flag = 0; // let go of lock; no one wants it else unpark(queue_remove(m->q)); // hold lock (for next thread!) m->guard = 0; } Why flag does not get set to 0 when another thread gets woken up? –the waking thread does not hold the guard


Download ppt "Chapter 28 Locks Chien-Chung Shen CIS, UD"

Similar presentations


Ads by Google