Conditional and Semaphore
Conditional and Semaphore
COMS(3010A)
Condition variables
and
Semaphores
Branden Ingram
[email protected]
Office Number : ???
Recap
• Locks
• Synchronization Challenges
• Test and Set
• LLSC
• Ticket Locks
• Using Queues
Condition variable
• There are many cases where a thread wishes to check whether a condition is true
before continuing its execution.
Condition variable
• There are many cases where a thread wishes to check whether a condition is true
before continuing its execution.
Example:
A parent thread might wish to check whether a child thread has completed.
This is often called a join().
Condition variable
• There are many cases where a thread wishes to check whether a condition is true
before continuing its execution.
Example:
A parent thread might wish to check whether a child thread has completed.
This is often called a join().
• This is hugely inefficient as the parent spins and wastes CPU time
How do condition variables work
• CV::signal() : This takes one thread off the condition variables waiting list and
marks it eligible to run (puts it on the schedulers ready list)
• CV::broadcast() : Takes all threads off the condition variables waiting list and
marks them as eligible to run
How do condition variables work
1 void thr_exit() {
2 Pthread_mutex_lock(&m);
3 Pthread_cond_signal(&c);
4 Pthread_mutex_unlock(&m);
5 }
6
7 void thr_join() {
8 Pthread_mutex_lock(&m);
9 Pthread_cond_wait(&c, &m);
10 Pthread_mutex_unlock(&m);
11 }
thr_exit() and thr_join() without variable done
• Producer
•Produce data items
•Wish to place data items in a buffer
• Consumer
•Grab data items out of the buffer consume them in some way
• A bounded buffer is used when you pipe the output of one program into another.
• Example: grep foo file.txt | wc –l
• The grep process is the producer.
• The wc process is the consumer.
• Between them is an in-kernel bounded buffer.
Producer puts an integer into the shared buffer loops number of times.
Consumer gets the data out of that shared buffer.
Simple Example (broken solution)
A single condition variable cond and associated lock mutex with IF
1 cond_t cond;
2 mutex_t mutex;
3
4 void *producer(void *arg) {
5 int i;
6 for (i = 0; i < loops; i++) {
7 Pthread_mutex_lock(&mutex); // p1
8 if (count == 1) // p2
9 Pthread_cond_wait(&cond, &mutex); // p3
10 put(i); // p4
11 Pthread_cond_signal(&cond); // p5
12 Pthread_mutex_unlock(&mutex); // p6
13 }
14 }
15
16 void *consumer(void *arg) {
17 int i;
18 for (i = 0; i < loops; i++) {
19 Pthread_mutex_lock(&mutex); // c1
Simple Example (broken solution)
20 if (count == 0) // c2
21 Pthread_cond_wait(&cond, &mutex); // c3
22 int tmp = get(); // c4
23 Pthread_cond_signal(&cond); // c5
24 Pthread_mutex_unlock(&mutex); // c6
25 printf("%d\n", tmp);
26 }
27 }
With just a single producer and a single consumer, the code works.
A consumer should not wake other consumers, only producers, and vice-versa
Simple Example (Solution 3)
•Use two condition variables and while
• Producer threads wait on the condition empty, and signals fill.
• Consumer threads wait on fill and signal empty.
1 cond_t empty, fill;
2 mutex_t mutex;
3
4 void *producer(void *arg) {
5 int i;
6 for (i = 0; i < loops; i++) {
7 Pthread_mutex_lock(&mutex);
8 while (count == 1)
9 Pthread_cond_wait(&empty, &mutex);
10 put(i);
11 Pthread_cond_signal(&fill);
12 Pthread_mutex_unlock(&mutex);
13 }
14 }
15
Simple Example (Solution 3)
•Use two condition variables and while
• Producer threads wait on the condition empty, and signals fill.
• Consumer threads wait on fill and signal empty.
(Cont.)
16 void *consumer(void *arg) {
17 int i;
18 for (i = 0; i < loops; i++) {
19 Pthread_mutex_lock(&mutex);
20 while (count == 0)
21 Pthread_cond_wait(&fill, &mutex);
22 int tmp = get();
23 Pthread_cond_signal(&empty);
24 Pthread_mutex_unlock(&mutex);
25 printf("%d\n", tmp);
26 }
27 }
Simple Example (Final Solution 4)
• More concurrency and efficiency -> Add more buffer slots.
• Allow concurrent production or consuming to take place.
• Reduce context switches.
1 int buffer[MAX];
2 int fill = 0;
3 int use = 0;
4 int count = 0;
5
6 void put(int value) {
7 buffer[fill] = value;
8 fill = (fill + 1) % MAX;
9 count++;
10 }
11
12 int get() {
13 int tmp = buffer[use];
14 use = (use + 1) % MAX;
15 count--;
16 return tmp;
17 }
1 cond_t empty, fill;
2 mutex_t mutex;
3
4 void *producer(void *arg) {
5 int i;
6 for (i = 0; i < loops; i++) {
7 Pthread_mutex_lock(&mutex); // p1
8 while (count == MAX) // p2
9 Pthread_cond_wait(&empty, &mutex); // p3
10 put(i); // p4
11 Pthread_cond_signal(&fill); // p5
12 Pthread_mutex_unlock(&mutex); // p6
13 }
14 }
15
16 void *consumer(void *arg) {
17 int i;
18 for (i = 0; i < loops; i++) {
19 Pthread_mutex_lock(&mutex); // c1
20 while (count == 0) // c2
21 Pthread_cond_wait(&fill, &mutex); // c3
22 int tmp = get(); // c4
23 Pthread_cond_signal(&empty); // c5
24 Pthread_mutex_unlock(&mutex); // c6
25 printf("%d\n", tmp);
26 }
27 }
Semaphore
• sem_wait()
• If the value of the semaphore was one or higher when called sem_wait(),
return right away.
• It will cause the caller to suspend execution waiting for a subsequent post.
• When negative, the value of the semaphore is equal to the number of waiting
threads.
Semaphore - Interacting
• sem_post()
1 sem_t m;
2 sem_init(&m, 0, X); // initialize semaphore to X; what should X be?
3
4 sem_wait(&m);
5 //critical section here
6 sem_post(&m);
Thread Trace: Single Thread Using A
Semaphore
The parent call sem_wait() before the child has called sem_post().
1 int buffer[MAX];
2 int fill = 0;
3 int use = 0;
4
5 void put(int value) {
6 buffer[fill] = value; // line f1
7 fill = (fill + 1) % MAX; // line f2
8 }
9
10 int get() {
11 int tmp = buffer[use]; // line g1
12 use = (use + 1) % MAX; // line g2
13 return tmp;
14 }
The Producer/Consumer (Bounded-Buffer)
Problem
1 sem_t empty;
2 sem_t full;
3
4 void *producer(void *arg) {
5 int i;
6 for (i = 0; i < loops; i++) {
7 sem_wait(&empty); // line P1
8 put(i); // line P2
9 sem_post(&full); // line P3
10 }
11 }
12
13 void *consumer(void *arg) {
14 int i, tmp = 0;
15 while (tmp != -1) {
16 sem_wait(&full); // line C1
17 tmp = get(); // line C2
18 sem_post(&empty); // line C3
19 printf("%d\n", tmp);
20 }
21 }
22 …
• Zemaphore don’t maintain the invariant that the value of the semaphore.