[537] Semaphores Chapter 31 Tyler Harter 10/20/14 - - PowerPoint PPT Presentation

537 semaphores
SMART_READER_LITE
LIVE PREVIEW

[537] Semaphores Chapter 31 Tyler Harter 10/20/14 - - PowerPoint PPT Presentation

[537] Semaphores Chapter 31 Tyler Harter 10/20/14 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.


slide-1
SLIDE 1

[537] Semaphores

Chapter 31 Tyler Harter 10/20/14

slide-2
SLIDE 2

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.
slide-3
SLIDE 3

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.
  • examples?
  • what primitives did we use?
slide-4
SLIDE 4

Condition Variables

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
slide-5
SLIDE 5

Example: Bounded Buffer

void *producer(void *arg) { for (int i=0; i<loops; i++) { Mutex_lock(&m); while(numfull == max) Cond_wait(&empty, &m); do_fill(i); Cond_signal(&fill); Mutex_unlock(&m); } } void *consumer(void *arg) { while(1) { Mutex_lock(&m); while(numfull == 0) Cond_wait(&fill, &m); int tmp = do_get(); Cond_signal(&empty); Mutex_unlock(&m); printf(“%d\n”, tmp); } }

slide-6
SLIDE 6

Example: Bounded Buffer

void *producer(void *arg) { for (int i=0; i<loops; i++) { Mutex_lock(&m); while(numfull == max) Cond_wait(&empty, &m); do_fill(i); Cond_signal(&fill); Mutex_unlock(&m); } } void *consumer(void *arg) { while(1) { Mutex_lock(&m); while(numfull == 0) Cond_wait(&fill, &m); int tmp = do_get(); Cond_signal(&empty); Mutex_unlock(&m); printf(“%d\n”, tmp); } }

slide-7
SLIDE 7

Discuss

Can we do producer/consumer with only locks (no CVs)?

slide-8
SLIDE 8

Discuss

Can we do producer/consumer with only locks (no CVs)?

  • Do you like CVs?
slide-9
SLIDE 9

Discuss

Can we do producer/consumer with only locks (no CVs)?

  • Do you like CVs? No!
slide-10
SLIDE 10

Discuss

Can we do producer/consumer with only locks (no CVs)?

  • Do you like CVs? No!
  • Why are CVs hard to use?
slide-11
SLIDE 11

Discuss

Can we do producer/consumer with only locks (no CVs)?

  • Do you like CVs? No!
  • Why are CVs hard to use?
  • What rules of thumb should we follow with CVs?
slide-12
SLIDE 12

CV rules of thumb

Keep state in addition to CV’s

  • Always do wait/signal with lock held
  • Whenever you acquire a lock, recheck state
slide-13
SLIDE 13

Design Tip

If it’s always recommended to use an abstraction the same way…

slide-14
SLIDE 14

Design Tip

If it’s always recommended to use an abstraction the same way…

  • …build a better abstraction
  • ver your first abstraction.
slide-15
SLIDE 15

More Concurrency Abstractions

Linux Workqueues: list of function ptr’s to call later.

  • Semaphores: today’s topic.
slide-16
SLIDE 16

Condition Variable

Queue:

slide-17
SLIDE 17

Condition Variable

Queue:

A

wait()

slide-18
SLIDE 18

Condition Variable

Queue:

A

slide-19
SLIDE 19

Condition Variable

Queue:

B

wait()

A

slide-20
SLIDE 20

Condition Variable

Queue:

B A

slide-21
SLIDE 21

Condition Variable

Queue:

B

signal()

slide-22
SLIDE 22

Condition Variable

Queue:

B

slide-23
SLIDE 23

Condition Variable

Queue: signal()

slide-24
SLIDE 24

Condition Variable

Queue:

slide-25
SLIDE 25

Condition Variable

Queue: signal()

slide-26
SLIDE 26

Condition Variable

Queue: signal() nothing to do!

slide-27
SLIDE 27

Condition Variable

Queue:

slide-28
SLIDE 28

Condition Variable

Queue:

C

wait()

slide-29
SLIDE 29

Condition Variable

Queue:

C

slide-30
SLIDE 30

Condition Variable

Queue:

C

If we weren’t careful, C may sleep forever.

slide-31
SLIDE 31

Semaphore

Thread Queue: Signal Queue:

slide-32
SLIDE 32

Semaphore

Thread Queue: Signal Queue:

A

wait()

slide-33
SLIDE 33

Semaphore

Thread Queue: Signal Queue:

A

slide-34
SLIDE 34

Semaphore

Thread Queue: Signal Queue: signal()

slide-35
SLIDE 35

Semaphore

Thread Queue: Signal Queue:

slide-36
SLIDE 36

Semaphore

Thread Queue: Signal Queue: signal()

signal

slide-37
SLIDE 37

Semaphore

Thread Queue: Signal Queue:

signal

slide-38
SLIDE 38

Semaphore

Thread Queue: Signal Queue:

signal

wait()

A

slide-39
SLIDE 39

Semaphore

Thread Queue: Signal Queue: wait()

slide-40
SLIDE 40

Semaphore

Thread Queue: Signal Queue: wait() signal was not lost do to some race condition!

slide-41
SLIDE 41

Semaphore

Thread Queue: Signal Queue:

slide-42
SLIDE 42

Actual Implementation

Use counter instead of Signal Queue

  • all signals are the same
  • If the counter is positive, don’t bother to queue

a thread upon wait().

slide-43
SLIDE 43

Actual Implementation

Use counter instead of Signal Queue

  • all signals are the same
  • If the counter is positive, don’t bother to queue

a thread upon wait().

  • CV’s don’t keep extra state, so CV users must.

Semaphores keep extra state, so users sometimes don’t.

slide-44
SLIDE 44

Actual Definition (see handout)

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) }

slide-45
SLIDE 45

Actual Definition (see handout)

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

slide-46
SLIDE 46

Actual Definition (see handout)

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) }

value = 4: 4 waiting signals value = -3: 3 waiting threads

slide-47
SLIDE 47

Join example

Join is simpler with semaphores than CV’s.

slide-48
SLIDE 48

Join w/ CV

int done = 0; mutex_t m = MUTEX_INIT; cond_t c = COND_INIT; void *child(void *arg) { printf(“child\n”); Mutex_lock(&m); done = 1; cond_signal(&c); Mutex_unlock(&m); }

  • int main(int argc, char *argv[]) {

pthread_t c; printf(“parent: begin\n”); Pthread_create(c, NULL, child, NULL); Mutex_lock(&m); while(done == 0) Cond_wait(&c, &m); Mutex_unlock(&m); printf(“parent: end\n”); }

slide-49
SLIDE 49

Join w/ Semaphore

sem_t s; void *child(void *arg) { printf(“child\n”); sem_post(&s); }

  • int main(int argc, char *argv[]) {

sem_init(&s, ?); pthread_t c; printf(“parent: begin\n”); Pthread_create(c, NULL, child, NULL); sem_wait(&s); printf(“parent: end\n”); }

slide-50
SLIDE 50

Join w/ Semaphore

sem_t s; void *child(void *arg) { printf(“child\n”); sem_post(&s); }

  • int main(int argc, char *argv[]) {

sem_init(&s, ?); pthread_t c; printf(“parent: begin\n”); Pthread_create(c, NULL, child, NULL); sem_wait(&s); printf(“parent: end\n”); }

slide-51
SLIDE 51

Join w/ Semaphore

sem_t s; void *child(void *arg) { printf(“child\n”); sem_post(&s); }

  • int main(int argc, char *argv[]) {

sem_init(&s, ?); pthread_t c; printf(“parent: begin\n”); Pthread_create(c, NULL, child, NULL); sem_wait(&s); printf(“parent: end\n”); }

What is this int?

slide-52
SLIDE 52

Join w/ Semaphore

sem_t s; void *child(void *arg) { printf(“child\n”); sem_post(&s); }

  • int main(int argc, char *argv[]) {

sem_init(&s, ?); pthread_t c; printf(“parent: begin\n”); Pthread_create(c, NULL, child, NULL); sem_wait(&s); printf(“parent: end\n”); }

slide-53
SLIDE 53

Join w/ Semaphore

sem_t s; void *child(void *arg) { printf(“child\n”); sem_post(&s); }

  • int main(int argc, char *argv[]) {

sem_init(&s, 0); pthread_t c; printf(“parent: begin\n”); Pthread_create(c, NULL, child, NULL); sem_wait(&s); printf(“parent: end\n”); }

slide-54
SLIDE 54

Join w/ Semaphore

sem_t s; void *child(void *arg) { printf(“child\n”); sem_post(&s); }

  • int main(int argc, char *argv[]) {

sem_init(&s, 0); pthread_t c; printf(“parent: begin\n”); Pthread_create(c, NULL, child, NULL); sem_wait(&s); printf(“parent: end\n”); }

Run it! (sem-join.c)

slide-55
SLIDE 55

Worksheet

Problem 1: building locks with semaphores

  • Problem 2: building semaphores with locks and CV’s
slide-56
SLIDE 56

Equivalence Claim

Semaphores are equally powerful to Locks+CVs.

  • what does this mean?
slide-57
SLIDE 57

Equivalence Claim

Semaphores are equally powerful to Locks+CVs.

  • what does this mean?
  • Either may be more convenient, but that’s not relevant.
  • Equivalence means we can build each over the other.
slide-58
SLIDE 58

Proof Steps

Want to show we can do these three things:

Locks Semaphores CV’s Semaphores Locks Semaphores CV’s

slide-59
SLIDE 59

Proof Steps

Want to show we can do these three things:

Locks Semaphores CV’s Semaphores Locks Semaphores CV’s

done!

(problem 1)

done!

(problem 2)

slide-60
SLIDE 60

Building CV’s over Semaphores

Possible, but really hard to do right.

  • Read about Microsoft Research’s attempts:
  • http://research.microsoft.com/pubs/64242/ImplementingCVs.pdf
  • We won’t go beyond our simple join example.

CV’s Semaphores

slide-61
SLIDE 61

Building CV’s over Semaphores

Possible, but really hard to do right.

  • Read about Microsoft Research’s attempts:
  • http://research.microsoft.com/pubs/64242/ImplementingCVs.pdf
  • We won’t go beyond our simple join example.

CV’s Semaphores

slide-62
SLIDE 62

Bounded-Buffer w/ Semaphores

Write code.

slide-63
SLIDE 63

R/W Lock w/ Semaphores

Worksheet, Problem 3.

slide-64
SLIDE 64

Summary

Locks+CVs are good primitives, but not always convenient.

  • Possible to build other abstractions such as semaphores.
  • Advice: if you always use an abstraction the same way,

build another abstraction over the first!