In your group you are going to do the following:

1. Develop a pseudocode solution

First, come up with a pseudocode solution to synchronize the actions of Producer and Consumer threads that add (produce) and remove (consume) items using a shared, fixed-capacity buffer:

  1. Some number of Producer threads, each in a loop forever:

    1. produce the next item

    2. add it to the shared buffer (to one end of a circular queue)

  2. Some number of Consumer threads, each in a loop forever:

    1. remove the next item from the front of the buffer

    2. consume it

2. Some questions to consider

  1. Are there actions that need to be made atomic (require mutually exclusive access)?

  2. Are there any scheduling types of synchronization necessary?

  3. What synchronization primitives do you need, and how are they used (by whom and when)?

  4. Is any other state needed to synchronize the actions of threads?

3. Starting point assumptions

  1. You may assume the following shared global buffer state.

    static char *buff;     // the buffer
    static int  N;         // total capacity
    static int  size;      // current num elements
    static int  next_in;   // next insertion index
    static int  next_out;  // next remove index
    static int  num_items; // number of items each thread should produce/consume
  2. There exist functions to add items to and remove items from the buffer (implemented as a circular queue).

    void add_to_queue(char item);
    char remove_from_queue();

    These functions have no synchronization, and they do not check to see if there is space to add an item or if there is an item that can be removed. They just add to or remove from the buffer in a circular fashion and update other state variables as a result of their actions. This means you need to prevent adding to an already full buffer or trying to remove an item from the buffer that doesn’t exist, but you should prevent this without modifying those functions.

  3. 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, num_threads);
    pthread_barrier_wait(&mybarrier);
  4. Together, try implementing your algorithm in pthreads. You can do this on paper (and in fact this might be the preferred way at least as a first step), or you can try implementing and testing it using some starting point code:

    $ cd ~/cs31/
    $ mkdir -p inclass/w13/prodcons
    $ cd inclass/w13/prodcons
    $ cp -r ~richardw/public/cs31/inclass/w13/* ./
    $ ls
    Makefile  prodcons.c
    $ make
    $ ./prodcons 8 100 10
    8 prods/cons, each produces/consumes 100 items, buff size 10

    In addition to functions to add and remove items from the circular buffer, a print_buffer function is provided. Whenever you print, you should call fflush(stdout) to force debugging information to the terminal window.

    1. Implement code in main to spawn producer and consumer threads.

    2. Implement the producer and consumer main loop functions.

    3. Add all synchronization necessary to synchronize the actions of concurrent producer and consumer threads.

  5. Look at the man pages for pthread functions:

    $ man pthread_create
    $ man pthread_join
    $ man pthread_cond_init
    $ man pthread_mutex_init
  6. Look at the weekly lab page for other pthread examples.