condition variables revisited copyright ©: university of illinois cs 241 staff1

Post on 24-Dec-2015

216 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Condition Variables Revisited

Copyright ©: University of Illinois CS 241 Staff 1

Condition Variable

Without condition variables, Threads continually poll to check if the

condition is met Busy waiting!

With condition variables Same goal without polling

Copyright ©: University of Illinois CS 241 Staff 2

Inside a condition variable

struct pthread_cond {

int waiting;

handle_t semaphore;

};

Copyright ©: University of Illinois CS 241 Staff 3

Number of threads waiting on the condition variable

A semaphore for synchronization

Inside a condition variable

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) {

atomic_increment(&cond->waiting);

pthread_mutex_unlock(mutex);

if (wait(cond->semaphore, INFINITE) < 0)

return errno;

atomic_decrement(&cond->waiting);

pthread_mutex_lock(mutex);

return 0;

}

Copyright ©: University of Illinois CS 241 Staff 4

pthread_mutex_lock is always called before pthread_cond_wait to acquire lock

Hav

e lo

ck

int pthread_cond_signal(pthread_cond_t *cond) {

if (cond->waiting)

semrel(cond->semaphore, 1);

return 0;

}

Hav

e lo

ck

Thread always has lock when returning from pthread_cond_wait

More Complex Example

Master thread Spawns a number of concurrent slaves Waits until all of the slaves have finished to exit Tracks current number of slaves executing

A mutex is associated with count and a condition variable with the mutex

Copyright ©: University of Illinois CS 241 Staff 5

Example

#include <stdio.h>

#include <pthread.h>

#define NO_OF_PROCS 4

typedef struct _SharedType {

int count; /* number of active slaves */

pthread_mutex_t lock; /* mutex for count */

pthread_cond_t done; /* sig. by finished slave */

} SharedType, *SharedType_ptr;

SharedType_ptr shared_data;

Copyright ©: University of Illinois CS 241 Staff 6

Example: Main

main(int argc, char **argv) {

int res;

/* allocate shared data */

if ((sh_data = (SharedType *) malloc(sizeof(SharedType))) == NULL) {

exit(1);

}

sh_data->count = 0;

/* allocate mutex */

if ((res = pthread_mutex_init(&sh_data->lock, NULL)) != 0) {

exit(1);

}

/* allocate condition var */

if ((res = pthread_cond_init(&sh_data->done, NULL)) != 0) {

exit(1);

}

/* generate number of slaves to create */

srandom(0);

/* create up to 15 slaves */

master((int) random()%16);

}

Copyright ©: University of Illinois CS 241 Staff 7

Example: Main

main(int argc, char **argv) {

int res;

/* allocate shared data */

if ((sh_data = (SharedType *) malloc(sizeof(SharedType))) == NULL) {

exit(1);

}

sh_data->count = 0;

/* allocate mutex */

if ((res = pthread_mutex_init(&sh_data->lock, NULL)) != 0) {

exit(1);

}

/* allocate condition var */

if ((res = pthread_cond_init(&sh_data->done, NULL)) != 0) {

exit(1);

}

/* generate number of slaves to create */

srandom(0);

/* create up to 15 slaves */

master((int) random()%16);

}

Copyright ©: University of Illinois CS 241 Staff 8

pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_cont_t data_cond = PTHREAD_COND_INITIALIZER;

Example: Master

master(int nslaves) {

int i;

pthread_t id;

for (i = 1; i <= nslaves; i += 1) {

pthread_mutex_lock(&sh_data->lock);

/* start slave and detach */

shared_data->count += 1;

pthread_create(&id, NULL,

(void* (*)(void *))slave,

(void *)sh_data);

pthread_mutex_unlock(&sh_data->lock);

}

pthread_mutex_lock(&sh_data->lock);

while (sh_data->count != 0)

pthread_cond_wait(&sh_data->done, &sh_data->lock);

pthread_mutex_unlock(&sh_data->lock);

printf("All %d slaves have finished.\n", nslaves);

pthread_exit(0);

}

Copyright ©: University of Illinois CS 241 Staff 9

Example: Slave

void slave(void *shared) {

int i, n;

sh_data = shared;

printf(“Slave.\n", n);

n = random() % 1000;

for (i = 0; i < n; i+= 1)

Sleep(10);

/* mutex for shared data */

pthread_mutex_lock(&sh_data->lock);

/* dec number of slaves */

sh_data->count -= 1;

/* done running */

printf("Slave finished %d cycles.\n", n);

/* signal that you are done working */

pthread_cond_signal(&sh_data->done);

/* release mutex for shared data */

pthread_mutex_unlock(&sh_data->lock);

}

Copyright ©: University of Illinois CS 241 Staff 10

Semaphores vs. CVs

Semaphore Integer value (>=0) Wait does not always

block Signal either releases

thread or inc’s counter If signal releases

thread, both threads continue afterwards

Condition Variables No integer value Wait always blocks

Signal either releases thread or is lost

If signal releases thread, only one of them continue

Copyright ©: University of Illinois CS 241 Staff 11

Classical Synchronization Problems

12Copyright ©: University of Illinois CS 241 Staff

This lecture

Goals Introduce classical synchronization

problems Topics

Producer-Consumer Problem Reader-Writer Problem Dining Philosophers Problem Sleeping Barber’s Problem

13Copyright ©: University of Illinois CS 241 Staff

Chefs cook items and put them on a conveyer belt

14Copyright ©: University of Illinois CS 241 Staff

Waiters pick items off the belt

Now imagine many chefs!

15Copyright ©: University of Illinois CS 241 Staff

And many waiters!

A potential mess!

16Copyright ©: University of Illinois CS 241 Staff

Producer-Consumer Problem

Producers insert items Consumers remove items Shared resource: bounded buffer

Efficient implementation: circular buffer with an insert and a removal pointer

Chef = ProducerWaiter = Consumer

17Copyright ©: University of Illinois CS 241 Staff

Producer-Consumer

18Copyright ©: University of Illinois CS 241 Staff

Chef = ProducerWaiter = Consumer

Producer-Consumer

insertPtr

removePtr

19Copyright ©: University of Illinois CS 241 Staff

Chef = ProducerWaiter = Consumer

What does the chef do with a

new pizza?

Where does the waiter take a pizza from?

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

20Copyright ©: University of Illinois CS 241 Staff

Insert pizza

insertPtr

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

21Copyright ©: University of Illinois CS 241 Staff

Insert pizza

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

22Copyright ©: University of Illinois CS 241 Staff

Insert pizza

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

23Copyright ©: University of Illinois CS 241 Staff

Remove pizza

removePtr

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

24Copyright ©: University of Illinois CS 241 Staff

Insert pizza

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

25Copyright ©: University of Illinois CS 241 Staff

Insert pizza

Producer-Consumer

insertPtr

removePtr

BUFFER FULL: Producer must be blocked!

Chef = ProducerWaiter = Consumer

26Copyright ©: University of Illinois CS 241 Staff

Insert pizza

Producer-Consumer

insertPtrremovePtr

Chef = ProducerWaiter = Consumer

27Copyright ©: University of Illinois CS 241 Staff

Remove pizza

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

28Copyright ©: University of Illinois CS 241 Staff

Remove pizza

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

29Copyright ©: University of Illinois CS 241 Staff

Remove pizza

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

30Copyright ©: University of Illinois CS 241 Staff

Remove pizza

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

31Copyright ©: University of Illinois CS 241 Staff

Remove pizza

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

32Copyright ©: University of Illinois CS 241 Staff

Remove pizza

Producer-Consumer

insertPtr

removePtr

Chef = ProducerWaiter = Consumer

33Copyright ©: University of Illinois CS 241 Staff

Remove pizza

Producer-Consumer

insertPtr

removePtr

BUFFER EMPTY: Consumer must be blocked!

Chef = ProducerWaiter = Consumer

34Copyright ©: University of Illinois CS 241 Staff

Remove pizza

Producer-Consumer Summary

Producer Insert items Update insertion pointer

Consumer Execute destructive read on the buffer Update removal pointer

Both Update information about how full/empty the buffer is

Solution Must allow multiple producers and consumers

Copyright ©: University of Illinois CS 241 Staff 35

Challenges

Prevent buffer overflow Prevent buffer underflow Mutual exclusion when modifying the

buffer data structure

36Copyright ©: University of Illinois CS 241 Staff

Solutions

Prevent buffer overflow Block producer when full Counting semaphore to count #free slots 0 block producer

Prevent buffer underflow Mutual exclusion when modifying the

buffer data structure

37Copyright ©: University of Illinois CS 241 Staff

Solutions

Prevent buffer overflow Block producer when full Counting semaphore to count #free slots 0 block producer

Prevent buffer underflow Block consumer when empty Counting semaphore to count #items in buffer 0 block consumer

Mutual exclusion when modifying the buffer data structure

38Copyright ©: University of Illinois CS 241 Staff

Solutions

Prevent buffer overflow Block producer when full Counting semaphore to count #free slots 0 block producer

Prevent buffer underflow Block consumer when empty Counting semaphore to count #items in buffer 0 block consumer

Mutual exclusion when modifying the buffer data structure Mutex protects shared buffer & pointers

39Copyright ©: University of Illinois CS 241 Staff

Assembling the solution

Producer sem_wait(slots), sem_signal(slots) Initialize slots to N

Consumer sem_wait(items), sem_signal(items) Initialize semaphore items to 0

Synchronization mutex_lock(m), mutex_unlock(m)

Buffer management insertptr = (insertptr+1) % N removalptr = (removalptr+1) % N

Copyright ©: University of Illinois CS 241 Staff 40

Producer-Consumer Code

buffer[ insertPtr ] = data;

insertPtr = (insertPtr + 1) % N;

result = buffer[removePtr];

removePtr = (removePtr +1) % N;

Copyright ©: University of Illinois CS 241 Staff 41

Critical Section: move insert pointer

Critical Section: move remove pointer

Producer-Consumer Code

sem_wait(slots);

mutex_lock(mutex);

buffer[ insertPtr ] = data;

insertPtr = (insertPtr + 1) % N;

mutex_unlock(mutex);

sem_signal(items);

sem_wait(items);

mutex_lock(mutex);

result = buffer[removePtr];

removePtr = (removePtr +1) % N;

mutex_unlock(mutex);

sem_signal(slots);

Copyright ©: University of Illinois CS 241 Staff 42

Block if there are no free slots

Block if there

are no items

to take

Counting semaphore – check and decrement the number of free slots

Counting semaphore – check and decrement the number of available items

Done – increment the number of available items

Done – increment the number of free slots

Shared Resource

Readers-Writers Problem

Shared Resource

Readers-Writers Problem

Shared Resource

Readers-Writers Problem

II. Reader-Writer Problem

Readers read data Writers write data Rules

Multiple readers may read the data simultaneously Only one writer can write the data at any time A reader and a writer cannot access data simultaneously

Locking table Whether any two can be in the critical section

simultaneously

Copyright ©: University of Illinois CS 241 Staff 48

Reader Writer

Reader OK No

Writer No No

reader() { while(TRUE) { <other stuff>; sem_wait(mutex); readCount++;

if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex);

/* Critical section */ access(resource);

sem_wait(mutex); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_post(mutex); }}

writer() { while(TRUE) { <other computing>; sem_wait(writeBlock); /* Critical section */ access(resource); sem_signal(writeBlock); }}

Reader-Writer: First Solutionint readCount = 0;semaphore mutex = 1;semaphore writeBlock = 1;

reader() { while(TRUE) { <other stuff>; sem_wait(mutex); readCount++;

if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex);

/* Critical section */ access(resource);

sem_wait(mutex); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_post(mutex); }}

writer() { while(TRUE) { <other computing>; sem_wait(writeBlock); /* Critical section */ access(resource); sem_signal(writeBlock); }}

First reader competes with writersLast reader signals writers

Reader-Writer: First Solutionint readCount = 0;semaphore mutex = 1;semaphore writeBlock = 1;

First reader blocks writers

Last reader unblocks writers

Writer blocks all readers

reader() { while(TRUE) { <other stuff>; sem_wait(mutex); readCount++;

if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex);

/* Critical section */ access(resource);

sem_wait(mutex); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_post(mutex); }}

writer() { while(TRUE) { <other computing>; sem_wait(writeBlock); /* Critical section */ access(resource); sem_signal(writeBlock); }}

Any writer must wait for all readers→ Readers can starve writers→ Updates can be delayed forever→ Not desirable!

Reader-Writer: First Solutionint readCount = 0;semaphore mutex = 1;semaphore writeBlock = 1;

First reader blocks writers

Last reader unblocks writers

reader() { while(TRUE) { <other computing>; sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock);

access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); }}

writer() { while(TRUE) { <other computing>; sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); }}

Reader-Writer: Second Solution

int readCount=0, writeCount=0;semaphore mutex1=1, mutex2=1;Semaphore readBlock=1,writeBlock=1

reader() { while(TRUE) { <other computing>; sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock);

access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); }}

writer() { while(TRUE) { <other computing>; sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); }}

Reader-Writer: Second Solution

int readCount=0, writeCount=0;semaphore mutex1=1, mutex2=1;Semaphore readBlock=1,writeBlock=1

Wait for writers First writer

blocks readers

Last writer unblocks

readers

reader() { while(TRUE) { <other computing>; sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock);

access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); }}

writer() { while(TRUE) { <other computing>; sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); }}

Reader-Writer: Second Solution

int readCount=0, writeCount=0;semaphore mutex1=1, mutex2=1;Semaphore readBlock=1,writeBlock=1

Writers can starve readers→ Reads can be delayed forever→ Not desirable, either!

Better R-W solution idea

Idea: serve requests in order Once a writer requests access, any

entering readers have to block until the writer is done

Advantage? Disadvantage?

55Copyright ©: University of Illinois CS 241 Staff

reader() { while(TRUE) { <other computing>; sem_wait(writePending); sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); sem_signal(writePending); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); }}

writer() { while(TRUE) { <other computing>; sem_wait(writePending); sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_signal(writePending); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); }}

Reader-Writer: Fairer Solution?

int readCount = 0, writeCount = 0;semaphore mutex1 = 1, mutex2 = 1;semaphore readBlock = 1, writeBlock = 1, writePending = 1;

reader() { while(TRUE) { <other computing>; sem_wait(writePending); sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); sem_signal(writePending); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); }}

writer() { while(TRUE) { <other computing>; sem_wait(writePending); sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_signal(writePending); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); }}

Reader-Writer: Fairer Solution?

int readCount = 0, writeCount = 0;semaphore mutex1 = 1, mutex2 = 1;semaphore readBlock = 1, writeBlock = 1, writePending = 1;

Wait for a pending writers that arrived before me

Writer blocks

subsequent readers

4131reader() { while(TRUE) { <other computing>; sem_wait(writePending); sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); sem_signal(writePending); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); }}

writer() { while(TRUE) { <other computing>; sem_wait(writePending); sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_signal(writePending); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); }}

Reader-Writer: Fairer Solution?

int readCount = 0, writeCount = 0;semaphore mutex1 = 1, mutex2 = 1;semaphore readBlock = 1, writeBlock = 1, writePending = 1;

43

1

reader() { while(TRUE) { <other computing>; sem_wait(writePending); sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); sem_signal(writePending); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); }}

writer() { while(TRUE) { <other computing>; sem_wait(writePending); sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_signal(writePending); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); }}

Reader-Writer: Fairer Solution?

int readCount = 0, writeCount = 0;semaphore mutex1 = 1, mutex2 = 1;semaphore readBlock = 1, writeBlock = 1, writePending = 1;

1

reader() { while(TRUE) { <other computing>; sem_wait(writePending); sem_wait(readBlock); sem_wait(mutex1); readCount++; if(readCount == 1) sem_wait(writeBlock); sem_signal(mutex1); sem_signal(readBlock); sem_signal(writePending); access(resource); sem_wait(mutex1); readCount--; if(readCount == 0) sem_signal(writeBlock); sem_signal(mutex1); }}

writer() { while(TRUE) { <other computing>; sem_wait(writePending); sem_wait(mutex2); writeCount++; if(writeCount == 1) sem_wait(readBlock); sem_signal(mutex2); sem_wait(writeBlock); access(resource); sem_signal(writeBlock); sem_signal(writePending); sem_wait(mutex2); writeCount--; if(writeCount == 0) sem_signal(readBlock); sem_signal(mutex2); }}

Reader-Writer: Fairer Solution?

int readCount = 0, writeCount = 0;semaphore mutex1 = 1, mutex2 = 1;semaphore readBlock = 1, writeBlock = 1, writePending = 1;

When a reader arrives and a writer is waiting, the reader is suspended behind the writer→ Less concurrency→ Lower performance

Summary

Classic synchronization problems Producer-Consumer Problem Reader-Writer Problem

Saved for next time: Sleeping Barber’s Problem Dining Philosophers Problem

61Copyright ©: University of Illinois CS 241 Staff

Dining Philosophers

N philosophers and N forks Philosophers eat/think Eating needs 2 forks Pick one fork at a time

2

62Copyright ©: University of Illinois CS 241 Staff

top related