diff --git a/threads-cv/Makefile b/threads-cv/Makefile index 5558a3e..e3dd51a 100644 --- a/threads-cv/Makefile +++ b/threads-cv/Makefile @@ -6,6 +6,7 @@ SRCS := join.c \ join_no_lock.c \ join_no_state_var.c \ join_modular.c \ + pc_single_cv.c \ pc.c OBJS := ${SRCS:c=o} diff --git a/threads-cv/README.md b/threads-cv/README.md new file mode 100644 index 0000000..4c6cbfd --- /dev/null +++ b/threads-cv/README.md @@ -0,0 +1,19 @@ + +# Condition Variables + +Code examples from condition variables chapter. Build by typing `make`; +run the resulting executable to see how it works. Insert `sleep()` calls +of various lengths to control timing and force bad things to happen. + +## Fork/Join Problem + +- `join_spin.c`: Working solution but wastes CPU. +- `join_no_lock.c`: What happens when you don't put a lock around the state change and signal +- `join_no_state_var.c`: What happens if you don't have a state variable +- `join.c`: A working solution +- `join_modular.c`: A modularized version + +## Producer/Consumer Problem +- `pc_single_cv.c`: What happens if you only use one condition variable +- `pc.c`: A working solution + diff --git a/threads-cv/pc_single_cv.c b/threads-cv/pc_single_cv.c new file mode 100644 index 0000000..4795cb7 --- /dev/null +++ b/threads-cv/pc_single_cv.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "common_threads.h" + +int max; +int loops; +int *buffer; + +int use_ptr = 0; +int fill_ptr = 0; +int num_full = 0; + +pthread_cond_t cv = PTHREAD_COND_INITIALIZER; +pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + +int consumers = 1; +int verbose = 1; + + +void do_fill(int value) { + buffer[fill_ptr] = value; + fill_ptr = (fill_ptr + 1) % max; + num_full++; +} + +int do_get() { + int tmp = buffer[use_ptr]; + use_ptr = (use_ptr + 1) % max; + num_full--; + return tmp; +} + +void *producer(void *arg) { + int i; + for (i = 0; i < loops; i++) { + Mutex_lock(&m); // p1 + while (num_full == max) // p2 + Cond_wait(&cv, &m); // p3 + do_fill(i); // p4 + Cond_signal(&cv); // p5 + Mutex_unlock(&m); // p6 + } + + // end case: put an end-of-production marker (-1) + // into shared buffer, one per consumer + for (i = 0; i < consumers; i++) { + Mutex_lock(&m); + while (num_full == max) + Cond_wait(&cv, &m); + do_fill(-1); + Cond_signal(&cv); + Mutex_unlock(&m); + } + + return NULL; +} + +void *consumer(void *arg) { + int tmp = 0; + // consumer: keep pulling data out of shared buffer + // until you receive a -1 (end-of-production marker) + while (tmp != -1) { + Mutex_lock(&m); // c1 + while (num_full == 0) // c2 + Cond_wait(&cv, &m); // c3 + tmp = do_get(); // c4 + Cond_signal(&cv); // c5 + Mutex_unlock(&m); // c6 + } + return NULL; +} + +int +main(int argc, char *argv[]) +{ + if (argc != 4) { + fprintf(stderr, "usage: %s \n", argv[0]); + exit(1); + } + max = atoi(argv[1]); + loops = atoi(argv[2]); + consumers = atoi(argv[3]); + + buffer = (int *) malloc(max * sizeof(int)); + assert(buffer != NULL); + + int i; + for (i = 0; i < max; i++) { + buffer[i] = 0; + } + + pthread_t pid, cid[consumers]; + Pthread_create(&pid, NULL, producer, NULL); + for (i = 0; i < consumers; i++) { + Pthread_create(&cid[i], NULL, consumer, (void *) (long long int) i); + } + Pthread_join(pid, NULL); + for (i = 0; i < consumers; i++) { + Pthread_join(cid[i], NULL); + } + return 0; +} +