Presentation is loading. Please wait.

Presentation is loading. Please wait.

Lecture 12 CV and Semaphores

Similar presentations


Presentation on theme: "Lecture 12 CV and Semaphores"— Presentation transcript:

1 Lecture 12 CV and Semaphores

2 Condition Variables Condition Variable: queue of sleeping threads.
Threads add themselves to the queue with wait. Threads wake up threads on the queue with signal. wait(cond_t *cv, mutex_t *lock) assumes the lock is held when wait() is called puts caller to sleep + releases the lock (atomically) when awoken, reacquires lock before returning signal(cond_t *cv) wake a single waiting thread (if >= 1 thread is waiting) if there is no waiting thread, just return, doing nothing

3 Ordering Example: Join
pthread_t p1, p2; printf("main: begin [balance = %d]\n", balance); Pthread_create(&p1, NULL, mythread, "A"); Pthread_create(&p2, NULL, mythread, "B"); // join waits for the threads to finish Pthread_join(p1, NULL); Pthread_join(p2, NULL); printf("main: done\n [balance: %d]\n [should: %d]\n", balance, max*2); return 0;

4 Implementing Join with CV’s (attempt 1)
void thread_exit() { Mutex_lock(&m); // a Cond_signal(&c); // b Mutex_unlock(&m); // c } void thread_join() { Mutex_lock(&m); // x Cond_wait(&c, &m); // y Mutex_unlock(&m); // z

5 Implementing Join with CV’s (attempt 2)
void thread_exit() { done = 1; // a Cond_signal(&c); // b } void thread_join() { Mutex_lock(&m); // w if (done == 0) // x Cond_wait(&c, &m); // y Mutex_unlock(&m); // z

6 Good Rule of Thumb Keep state in addition to CV’s!
CV’s are used to nudge threads when state changes. If state is already as needed, don’t wait for a nudge! Always do wait and signal while holding the lock!

7 Implementing Join with CV’s (Correct)
void thread_exit() { Mutex_lock(&m); done = 1; // a Cond_signal(&c); // b Mutex_unlock(&m); } void thread_join() { Mutex_lock(&m); // w if (done == 0) // x Cond_wait(&c, &m); // y Mutex_unlock(&m); // z

8 Implementing Join with CV’s (Correct?)
void thread_exit() { Mutex_lock(&m); done = 1; // a Mutex_unlock(&m); Cond_signal(&c); // b } void thread_join() { Mutex_lock(&m); // w if (done == 0) // x Cond_wait(&c, &m); // y Mutex_unlock(&m); // z

9 Implementing Join with CV’s (Correct?)
void thread_exit() { done = 1; // a Mutex_lock(&m); Cond_signal(&c); // b Mutex_unlock(&m); } void thread_join() { Mutex_lock(&m); // w if (done == 0) // x Cond_wait(&c, &m); // y Mutex_unlock(&m); // z

10 Producer/Consumer Problem Example: UNIX Pipes
A pipe may have many writers and readers. Internally, there is a finite-sized buffer. Writers add data to the buffer. Readers remove data from the buffer. Implementation: reads/writes to buffer require locking when buffers are full, writers must wait when buffers are empty, readers must wait

11 Producer/Consumer Problem
Producers generate data (like pipe writers). Consumers grab data and process it (like pipe readers). Producer/consumer problems are frequent in systems. Pipes Web servers Memory allocators Device I/O

12 Queue get/put void put(int value) { buffer[fillptr] = value; fillptr = (fillptr + 1) % max; numfull++; } int get() { int tmp = buffer[useptr]; useptr = (useptr + 1) % max; numfull--; return tmp;

13 Solution v1 void *producer(void *arg) {
for (int i=0; i < loops; i++){ Mutex_lock(&m); //p1 while (numfull == max) //p2 Cond_wait(&C, &m); //p3 put(i); //p4 Cond_signal(&C); //p5 Mutex_unlock(&m); //p6 } void *consumer(void *arg) { while (1) { Mutex_lock(&m); //c1 while (numfull == 0) //c2 Cond_wait(&C, &m); //c3 int tmp = get(); //c4 Cond_signal(&C); //c5 Mutex_unlock(&m); //c6 printf("%d\n", tmp); } One producer with one consumer How about two consumers?

14 How to wake the right thread?
wait(cond_t *cv, mutex_t *lock) assumes the lock is held when wait() is called puts caller to sleep + releases the lock (atomically) when awoken, reacquires lock before returning signal(cond_t *cv) wake a single waiting thread (if >= 1 thread is waiting) if there is no waiting thread, just return, doing nothing broadcast(cond_t *cv) wake all waiting threads (if >= 1 thread is waiting) if there are no waiting thread, just return, doing nothing

15 Better solution (usually): use two CVs Solution v2
void *producer(void *arg) { for (int i=0; i < loops; i++){ Mutex_lock(&m); //p1 if (numfull == max) //p2 Cond_wait(&E, &m); //p3 put(i); //p4 Cond_signal(&F); //p5 Mutex_unlock(&m); //p6 } void *consumer(void *arg) { while (1) { Mutex_lock(&m); //c1 if (numfull == 0) //c2 Cond_wait(&F, &m); //c3 int tmp = get(); //c4 Cond_signal(&E); //c5 Mutex_unlock(&m); //c6 printf("%d\n", tmp); }

16 Summary: rules of thumb
Keep state in addition to CV’s Always do wait/signal with lock held Whenever you acquire a lock, recheck state

17 Solution v3 void *producer(void *arg) {
for (int i=0; i < loops; i++){ Mutex_lock(&m); //p1 while (numfull == max) //p2 Cond_wait(&E, &m); //p3 put(i); //p4 Cond_signal(&F); //p5 Mutex_unlock(&m); //p6 } void *consumer(void *arg) { while (1) { Mutex_lock(&m); //c1 while (numfull == 0) //c2 Cond_wait(&F, &m); //c3 int tmp = get(); //c4 Cond_signal(&E); //c5 Mutex_unlock(&m); //c6 printf("%d\n", tmp); }

18 Solution v4 void *producer(void *arg) {
for (int i=0; i < loops; i++){ Mutex_lock(&m); //p1 while (numfull == max) //p2 Cond_wait(&E, &m); //p3 Mutex_unlock(&m); //p3a put(i); //p4 Mutex_lock(&m); //p5a Cond_signal(&F); //p5 Mutex_unlock(&m); //p6 } void *consumer(void *arg) { while (1) { Mutex_lock(&m); //c1 while (numfull == 0) //c2 Cond_wait(&F, &m); //c3 Mutex_unlock(&m); //c3a int tmp = get(); //c4 Mutex_lock(&m); //c5a Cond_signal(&E); //c5 Mutex_unlock(&m); //c6 printf("%d\n", tmp); }

19 Semaphore Semaphores keep extra state, so users sometimes don’t.
Unlike CV, signal was not lost due to some race condition! Can be used as both locks and condition variables.

20 Actual Definition wait and post are atomic
sem_init(sem_t *s, int initval) { s->value = initval } sem_wait(sem_t *s) { s->value -= 1 wait if s->value < 0 sem_post(sem_t *s) { s->value += 1 wake one waiting thread (if there are any) wait and post are atomic value = 4: 4 waiting signals value = -3: 3 waiting threads

21 int done = 0; mutex_t m = MUTEX_INIT; cond_t c = COND_INIT; void
int done = 0; mutex_t m = MUTEX_INIT; cond_t c = COND_INIT; void *child(void *arg) { Mutex_lock(&m); done = 1; cond_signal(&c); Mutex_unlock(&m); } int main(int argc, char *argv[]) { pthread_t c; Pthread_create(c, NULL, child, NULL); while(done == 0) Cond_wait(&c, &m); Join with CV

22 sem_t s; void. child(void
sem_t s; void *child(void *arg) { sem_post(&s); } int main(int argc, char *argv[]) { sem_init(&s, ?); pthread_t c; Pthread_create(c, NULL, child, NULL); sem_wait(&s); Join with Semaphore

23 Build locks with semaphores
typedef struct __lock_t { // whatever data structs you need goes here } lock_t; void init(lock_t *lock) { // init code goes here } void acquire(lock_t *lock) { // lock acquire code goes here void release(lock_t *lock) { // lock release code goes here

24 Queue get/put void put(int value) { buffer[fillptr] = value; fillptr = (fillptr + 1) % max; numfull++; } int get() { int tmp = buffer[useptr]; useptr = (useptr + 1) % max; numfull--; return tmp;

25 P/C problem semaphore v1
int main(int argc, char *argv[]) { // ... sem_init(&empty, ?); sem_init(&full, ?); } sem_t empty, full; void *consumer(void *arg) { int i, tmp = 0; while (tmp != -1) { sem_wait(&full); // C1 tmp = get(); // C2 sem_post(&empty); // C3 printf("%d\n", tmp); } void *producer(void *arg) { int i; for (i = 0; i < loops; i++) { sem_wait(&empty); // P1 put(i); // P2 sem_post(&full); // P3 }

26 P/C problem semaphore v2
int main(int argc, char *argv[]) { // ... sem_init(&empty, MAX); sem_init(&full, 0); sem_init(&mutex, 1); } sem_t empty, full, mutex; void *consumer(void *arg) { int i, tmp = 0; while (tmp != -1) { sem_wait(&mutex); // C0 sem_wait(&full); // C1 tmp = get(); // C2 sem_post(&empty); // C3 sem_post(&mutex); // C4 printf("%d\n", tmp); } void *producer(void *arg) { int i; for (i = 0; i < loops; i++) { sem_wait(&mutex); // P0 sem_wait(&empty); // P1 put(i); // P2 sem_post(&full); // P3 sem_post(&mutex); // P4 }

27 P/C problem semaphore v3
int main(int argc, char *argv[]) { // ... sem_init(&empty, MAX); sem_init(&full, 0); sem_init(&mutex, 1); } sem_t empty, full, mutex; void *consumer(void *arg) { int i, tmp = 0; while (tmp != -1) { sem_wait(&full); // C1 sem_wait(&mutex); // C1.5 tmp = get(); // C2 sem_post(&mutex); // C2.5 sem_post(&empty); // C3 printf("%d\n", tmp); } void *producer(void *arg) { int i; for (i = 0; i < loops; i++) { sem_wait(&empty); // P1 sem_wait(&mutex); // P1.5 put(i); // P2 sem_post(&mutex); // P2.5 sem_post(&full); // P3 }

28 Reader-Writer Locks

29 The Dining Philosophers

30 Build semaphores with locks and CV’s
typedef struct __sem_t { int value; cond_t cond; lock_t lock; } sem_t; void sem_init(sem_t *s, int value) { s->value = value; Cond_init(&s->cond); Lock_init(&s->lock); } void sem_wait(sem_t *s) {…} void sem_post(sem_t *s) {…}

31 Next: Concurrency bugs


Download ppt "Lecture 12 CV and Semaphores"

Similar presentations


Ads by Google