In your group you are going to do the following:
- On paper, come up with a PSEUDOCODE solution to synchronize the
actions of Producer and Consumer
threads that add and remove items to a shared, fixed-capacity buffer:
- Some number of Producer threads, each in a loop forever:
- produce the next item
- add it to the shared buffer (to one end of a circular queue)
- Some number of Consumer threads, each in a loop forever:
- remove the next item from the front of the buffer
- consume it
Some questions to consider:
- are there actions that need to be made atomic
(require mutually exclusive access)?
- are there any scheduling types of synchronization necessary?
- what synchronization primitives do you need?
and how are they used (by whom and when)?
- is any other state needed to synchronize the actions of threads?
You may assume:
- The following shared global buffer state has been declared:
static char *buff; // the buffer
static int N; // total buffer capacity
static int size; // current num items in the buffer
static int next_in; // next insertion index in the buffer
static int next_out; // next remove index in the buffer
static int num_items; // number of items each tid should produce or consume
- There exist functions to add and remove items to the buffer as
a circular queue (add to one end, remove from the other).
void add_to_queue(char item);
char remove_from_queue();
These functions have no synchronization, nor do they check if there is space
on an add or something to remove on a remove. They just add or remove
to buff in a circular fashion and update other state variables as a
result of their actions.
- Some pthread functions:
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
pthread_cond_wait(&mycond, &mutex);
pthread_cond_signal(&mycond);
pthread_barrier_init(&mybarrier, NULL, numtids);
pthread_barrier_wait(&mybarrier);
When you are happy with your pseudocode algorithm talk a professor
or ninja through your algorithm BEFORE MOVING ON TO THE NEXT STEP.
- Next, on paper implement your solution for the
producer and consumer threads. First show declarations and
initialization for any syncrhonization variables needed. Then
write the main part of the code for two thread main functions:
one for producer threads and one for consumer threads. If it helps
you can copy over the code from the next step, but write up your
solution on paper first (you can skip some details that may be
needed in a full implementation).
- Finally, you can try implementing your solution in a
pthreads program, and test it out. Copy over starting point pthread code:
cd cs31/inclass
cp -r ~newhall/public/cs31/inclass/prodcons .
cd prodcons
make
./prodcons 8 100 10
8 Producer and Consmer tids, each producing 100 items, buff size 10
There are already routines to add and remove items from the circular
buffer, and a debug print_buffer function (call fflush(stdout) after
any debug output to force it to the terminal window).
- Implement code in main to spawn producer and consumer tids
- Implement the producer and consumer main loop functions
- Add all synchronization necessary to synchronize the actions of
concurrent producer and consumer threads
Look at the man pages for pthread functions:
man pthread_create
man pthread_join
man pthread_cond_init
man pthread_mutex_init
- Before the end of class, share your joint solution with your team mates.
Here is one way to do this from the cs machine you are ssh'ed into:
% mail username1 < prodcons.c
% mail username2 < prodcons.c