Presentation is loading. Please wait.

Presentation is loading. Please wait.

CS252: Systems Programming Ninghui Li Based on Slides by Prof. Gustavo Rodriguez-Rivera Topic 13: Condition Variable, Read/Write Lock, and Deadlock.

Similar presentations


Presentation on theme: "CS252: Systems Programming Ninghui Li Based on Slides by Prof. Gustavo Rodriguez-Rivera Topic 13: Condition Variable, Read/Write Lock, and Deadlock."— Presentation transcript:

1 CS252: Systems Programming Ninghui Li Based on Slides by Prof. Gustavo Rodriguez-Rivera Topic 13: Condition Variable, Read/Write Lock, and Deadlock

2 Pseudo-Code Implementing Semaphore Using Mutex Lock sem_wait(sem_t *sem){ lock(sem->mutex); sem -> count--; if(sem->count < 0){ unlock(sem->mutex); wait(); } else { unlock(sem->mutex) } sem_post(sem_t *sem){ lock(sem -> mutex); sem ->count++; if(sem->count < 0){ wake up a thread; } unlock(sem->mutex); } Assume that wait() causes a thread to be blocked. What could go wrong? How to fix it? Think about a context switch here.

3 Condition Variable What we need is the ability to wait on a condition while simultaneously giving up the mutex lock. Condition Variable (CV): A thread can wait on a CV; it will be blocked until another thread call signal on the CV A condition variable is always used in conjunction with a mutex lock. The thread calling wait should hold the lock, and the wait call will releases the lock while going to wait

4 Using Condition Variable Declaration: #include pthread_cond_t cv; Initialization: pthread_cond_init(&cv, pthread_condattr_t *attr); Wait on the condition variable: int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex); The calling threshold should hold mutex; it will be released atomically while start waiting on cv Upon successful return, the thread has re-aquired the mutex; however, the thread waking up and reaquiring the lock is not atomic.

5 Using Condition Variable Waking up waiting threads: int pthread_cond_signal(pthread_cond_t *cv); Unblocks one thread waiting on cv int pthread_cond_broadcast(pthread_cond_t *cv); Unblocks all threads waiting on cv The two methods can be called with or without holding the mutex that the thread calls wait with; but it is better to call it while holding the mutex

6 What is a Condition Variable? Each Condition Variable has a queue of blocked threads The cond_wait(cv, mutex) call adds the calling thread to cv’s queue, while releasing mutex; The call returns when the thread is unblocked (by another thread calling cond_signal), and the thread obtaining the mutex The cond_signal(cv) call removes one thread from the queue and unblocks it.

7 Implementing Semaphore using Mutex and Cond Var struct semaphore { pthread_cond_t cond; pthread_mutex_t mutex; int count; }; typedef struct semaphore semaphore_t; int semaphore_wait (semaphore_t *sem) { int res = pthread_mutex_lock(&(sem->mutex)); if (res != 0) return res; // error sem->count --; while (sem->count < 0) { res= pthread_cond_wait(&(sem->cond),&(sem->mutex)); } pthread_mutex_unlock(&(sem->mutex)); return res; }

8 Implementing Semaphore using Mutex and Cond Var int semaphore_post (semaphore_t *sem) { int res = pthread_mutex_lock(&(sem->mutex)); if (res != 0) return res; sem->count ++; if (sem->count <= 0) { res = pthread_cond_signal(&(sem->cond)); } pthread_mutex_unlock(&(sem->mutex)); return res; }

9 An Alternative and Buggy Implementation int semaphore_wait (semaphore_t *sem) { pthread_mutex_lock(&(sem->mutex)); if (sem->count <= 0) { pthread_cond_wait(&(sem->cond),&(sem->mutex)); } sem->count --; pthread_mutex_unlock(&(sem->mutex)); return res; } int semaphore_post (semaphore_t *sem) { pthread_mutex_lock(&(sem->mutex)); sem->count ++; pthread_cond_signal(&(sem->cond)); pthread_mutex_unlock(&(sem->mutex)); } What bad thing could happen if the two lines are switched?

10 Where is the Bug? Assume sem->count == 1 T1 T2 T3 T1 calls semaphore_wait() if (sem->count <= 0) { pthread_cond_wait(…); } sem->count --; 0 T1 continues T2 calls semaphore_wait() if (sem->count <= 0) { pthread_cond_wait(…); } T2 waits T1 calls semaphore_post() sem->count ++; 1 pthread_cond_signal(…);

11 Where is the Bug? Assume sem->count == 1 T1 T2 T3 T2 wakes up T3 calls semaphore_wait() if (sem->count <= 0) { pthread_cond_wait(…); } sem->count --;0 T3 continues T2 obtains mutex sem->count --;-1 T2 continues Both T2 and T3 are able to proceed now. This will not happen if while (sem->count <= 0) { pthread_cond_wait(…); } is used

12 Using while versus if when using cond_wait int semaphore_wait (…) { pthread_mutex_lock (…) while (sem->count<=0) { pthread_cond_wait (&(sem->cond), &(sem->mutex)); } sem->count --; thread_mutex_unlock (…) } int semaphore_wait (…) { pthread_mutex_lock (…) if (sem->count <=0) { pthread_cond_wait (&(sem->cond), &(sem->mutex)); } sem->count --; thread_mutex_unlock (…) } The left version is correct and the right version is wrong. Because waking up and obtaining mutex is not atomic. The condition sem->count<=0 may no longer hold when cond_wait returns control to the thread. Using while is also a defense against spurious wakeup

13 Usage of Semaphore: Bounded Buffer Implement a queue that has two functions enqueue() - adds one item into the queue. It blocks if queue if full dequeue() - remove one item from the queue. It blocks if queue is empty Strategy: Use an _emptySem semaphore that dequeue() will use to wait until there are items in the queue Use a _fullSem semaphore that enqueue() will use to wait until there is space in the queue.

14 Bounded Buffer #include enum {MaxSize = 10}; class BoundedBuffer{ int _queue[MaxSize]; int _head; int _tail; mutex_t _mutex; sem_t _emptySem; sem_t _fullSem; public: BoundedBuffer(); void enqueue(int val); int dequeue(); }; BoundedBuffer:: BoundedBuffer() { _head = 0; _tail = 0; pthtread_mutex_init(&_mutex, NULL); sem_init(&_emptySem, 0, 0); sem_init(&_fullSem, 0, MaxSize); }

15 Bounded Buffer void BoundedBuffer:: enqueue(int val) { sem_wait(&_fullSem); mutex_lock(_mutex); _queue[_tail]=val; _tail = (_tail+1)%MaxSize; mutex_unlock(_mutex); sem_post(_emptySem); } int BoundedBuffer:: dequeue() { sem_wait(&_emptySem); mutex_lock(_mutex); int val = _queue[_head]; _head = (_head+1)%MaxSize; mutex_unlock(_mutex); sem_post(_fullSem); return val; }

16 Bounded Buffer Assume queue is empty T1 T2 T3 v=dequeue() sem_wait(&_emptySem); _emptySem.count==-1 wait v=dequeue() sem_wait(&_emptySem); _emptySem.count==-2 wait enqueue(6) sem_wait(&_fullSem) put item in queue sem_post(&emptySem) _emptySem.count==-1 wakeup T1 T1 continues Get item from queue

17 Bounded Buffer Assume queue is empty T1 T2 …… T10 enqueue(1) sem_wait(&_fullSem); _fullSem.count==9 put item in queue enqueue(2) sem_wait(&_fullSem); _fullSem.count==8 put item in queue enqueue(10) sem_wait(&_fullSem); _fullSem.count==0 put item in queue

18 Bounded Buffer T11 T12 enqueue(11) sem_wait(&_fullSem); _fullSem.count==-1 wait val=dequeue() sem_wait(&_emptySem); _emptySem.count==9 get item from queue sem_post(&_fullSem) _fullSem.count==0 wakeup T11

19 Bounded Buffer Notes The counter for _emptySem represents the number of items in the queue The counter for _fullSem represents the number of spaces in the queue. Mutex locks are necessary to ensure that queue access is atomic.

20 Clicker Question 1 A POSIX pthread mutex may be normal/fast, recursive, or error-check based on their behavior on (1) a thread that holds the mutex calls lock again; (2) a thread that does not hold the mutex calls unlock. Which of the following describes a normal/fast mutex A. (1) calling thread can continue; (2) report error B. (1) report error; (2) succeeds C. (1) calling thread is blocked; (2) undefined by POSIX D. (1) report error; (2) report error E. None of the above

21 Clicker Question 2 A binary semaphore can sometimes be used in place of a mutex; what is its behavior in the following situations: (1) a thread that has called sem_wait calls sem_wait again; (2) a thread that has not called sem_wait calls sem_post. A. (1) calling thread continue; (2) report error B. (1) calling thread blocked; (2) succeeds C. (1) calling thread continue; (2) succeeds D. (1) calling thread blocked; (2) report error E. None of the above

22 Clicker Question 3 Consider the following code int semaphore_post (semaphore_t *sem) { pthread_mutex_lock (&(sem->mutex)); sem->count ++; pthread_mutex_unlock (&(sem->mutex)); pthread_cond_signal (&(sem->cond)); } What may go wrong? A. Too many threads may be able to continue B. A thread already waiting may not be correctly woke up C. A new thread may not be corrected woke up D. All of the above E. None of the above

23 Read/Write Locks They are locks for data structures that can be read by multiple threads simultaneously ( multiple readers ) but that can be modified by only one thread at a time. Example uses: Data Bases, lookup tables, dictionaries etc where lookups are more frequent than modifications.

24 Read/Write Locks Multiple readers may read the data structure simultaneously Only one writer may modify it and it needs to exclude the readers. Interface: ReadLock() – Lock for reading. Wait if there are writers holding the lock ReadUnlock() – Unlock for reading WriteLock() - Lock for writing. Wait if there are readers or writers holding the lock WriteUnlock() – Unlock for writing

25 Read/Write Locks Threads: R1 R2 R3 R4 W1 --- --- --- --- --- RL RL RL WL wait RU RU continue RL Wait WU continue rl = readLock; ru = readUnlock; wl = writeLock; wu = writeUnlock;

26 Read/Write Locks Implementation class RWLock { int _nreaders; //Controls access //to readers/writers sem_t _semAccess; mutex_t _mutex; public: RWLock(); void readLock(); void writeLock(); void readUnlock(); void writeUnlock(); }; RWLock::RWLock() { _nreaders = 0; sem_init( &semAccess, 1 ); mutex_init( &_mutex ); }

27 Read/Write Locks Implementation void RWLock::readLock() { mutex_Lock( &_mutex ); _nreaders++; if( _nreaders == 1 ) { //This is the // first reader //Get sem_Access sem_wait(&_semAccess); } mutex_unlock( &_mutex ); } void RWLock::readUnlock() { mutex_lock( &_mutex ); _nreaders--; if( _nreaders == 0 ) { //This is the last reader //Allow one writer to //proceed if any sem_post( &_semAccess ); } mutex_unlock( &_mutex ); }

28 Read/Write Locks Implementation void RWLock::writeLock() { sem_wait( &_semAccess ); } void RWLock::writeUnlock() { sem_post( &_semAccess ); }

29 Read/Write Locks Example Threads: R1 R2 R3 W1 W2 ----------- ------------ -------- -------- -------- readLock nreaders++(1) if (nreaders==1) sem_wait continue readLock nreaders++(2) readLock nreaders++(3) writeLock sem_wait (block)

30 Read/Write Locks Example Threads: R1 R2 R3 W1 W2 ----------- ------------ -------- -------- -------- writeLock sem_wait (block) readUnlock() nreaders—(2) readUnlock() nreaders—(1) readUnlock() nreaders—(0) if (nreaders==0) sem_post W1 continues writeUnlock sem_post W2 continues

31 Read/Write Locks Example Threads: (W2 is holding lock in write mode) R1 R2 R3 W1 W2 ----------- ------------ -------- -------- -------- readLock mutex_lock nreaders++(1) if (nreaders==1) sema_wait block readLock mutex_lock block writeUnlock sema_post R1 continues mutex_unlock R2 continues

32 Notes on Read/Write Locks  Fairness in locking: First-come-first serve  Mutexes and semaphores are fair. The thread that has been waiting the longest is the first one to wake up.  This implementation of read/write locks suffers from “starvation” of writers. That is, a writer may never be able to write if the number of readers is always greater than 0.

33 Write Lock Starvation (Overlapping readers) Threads: R1 R2 R3 R4 W1 --- --- --- --- --- RL RL RL WL wait RU RL RU RU RL rl = readLock; ru = readUnlock; wl = writeLock; wu = writeUnlock;

34 Review Questions What are Condition Variables? What is the behavior of wait/signal on CV? How to implement semaphores using using CV and Mutex? How to implement bounded buffer using semaphores?

35 Review Questions What are read/write locks? What is the behavior of read/write lock/unlock? How to implement R/W locks using semaphore? Why the implementation given in the slides can cause writer starvation? How to Implement a read/write lock where writer is preferred (i.e., when a writer is waiting, no reader can gain read lock and must wait until all writers are finished)?


Download ppt "CS252: Systems Programming Ninghui Li Based on Slides by Prof. Gustavo Rodriguez-Rivera Topic 13: Condition Variable, Read/Write Lock, and Deadlock."

Similar presentations


Ads by Google