CSE 333 Exercise 17

Out:   Friday, March 6
Due:   Monday, March 9 by 11 AM
Rating:   2 (note)
CSE 333 Exercise Rating Scale

Each exercise this quarter is rated on a integer scale of 1 – 5, inclusive, with 1 being the "least time-consuming" and 5 being the "most time-consuming".

This difficulty scale is meant as a rough guide for you in predicting the amount of time to set aside for each exercise as you balance the work required for 333 with your other obligations. However, it is necessarily imperfect as everyone's set of circumstances and experiences with the exercises differ. If your experience with an exercise does not align with its rating, that is not a reflection of you or your abilities.

Goals

  • Create a concurrent program with pthreads.
  • Use locks to make code thread-safe.

Background

A common design pattern used in concurrent programming is the Producer-Consumer pattern. In this design pattern, there is a set of producers that generate work for a set of consumers. As the producers create work items, they place them into a buffer/queue. When a consumer is free to process a new work item, it will grab the next one from the buffer/queue or wait if the buffer/queue is empty.

Problem Description

In this exercise, you will take an initial sequential producer-consumer program and change it so the producers and consumer run concurrently. To make things more interesting, the producers will produce Mulgikapsad (note the capitalization!) and kama, and the consumers will be responsible for eating them!

The initial program will:

  1. Start a producer of Mulgikapsad,
  2. Start a producer of kama,
  3. Start a consumer that eats all the dishes.

Since this code runs sequentially, all of the Mulgikapsad are produced first, followed by all of the kama, and then all the dishes are eaten in the order in which they were produced.

Your job is to make the two producers and the consumer tasks run concurrently using pthreads and NOT C++11 threads. When the producers and consumer are run concurrently, the output of the program should change. For example, instead of all the dishes being made first and then all eaten, some Mulgikapsad could be made, a few could be eaten, and then a kama could be made and immediately eaten. Each producer should create its dishes and add them to the queue as they are created. The consumer should remove dishes from the queue and eat them. If no dishes are present in the queue when the consumer is ready to eat another one, it should wait a short while and then check again to see if a new snack is available.

Provided Files

You should start by downloading the following four files (right-click and "Save link as..." if needed):

  • ex17.cc, which contains the producer and consumer functions
  • SimpleQueue.h, the header file for SimpleQueue, a linked list queue that is used as the producer-consumer buffer
  • SimpleQueue.cc, the implementation of SimpleQueue
  • Makefile, a makefile that will build the ex17 program


Implementation Notes

Using the pthread Library

You may find the following functions useful when writing your program: pthread_create, pthread_exit, pthread_join, pthread_mutex_init, pthread_mutex_destroy, pthread_mutex_lock, and pthread_mutex_unlock. In addition, you should understand the mutable keyword, because it could be necessary for making our const methods thread-safe.

Thread Safety

In order for the producers and consumers to run concurrently without stepping on each others' toes, you will need to make SimpleQueue thread-safe by modifying it so it acquires a lock when it runs critical sections of code and releases it when the critical section finishes. A critical section of code is any section where a modifiable resource shared among threads is accessed. If critical sections are not protected by locks, bad things such as data races and data structure corruption can occur. You should use a pthread mutex for this part.

Concurrent Execution

Once SimpleQueue is thread-safe, you should change ex17.cc so that the producers and consumer run concurrently using pthreads. You should NOT modify the given producer or consumer functions. Instead, you should start threads with new wrapper functions that call the desired producer/consumer function.

Data Races

For this exercise, "thread-safe" code means that it is synchronized with respect to accessing data. However, there may be inconsistencies with the usage of that data after we have read it. For example, it is possible with a thread-safe implementation to have a consumer print that they have consumed a snack before the producer has a chance to print that it has been produced. This behavior is OK for this exercise, as long as you make sure that the accessing of the shared data is thread-safe.

Style Focus

Modifying Provided Code

You should change most of the starter code. You should only change what is necessary to make the program multithreaded and thread-safe. This means you may not change which methods are declared const in SimpleQueue.

Error Handling & Robustness

In the case of an error, be sure to release all resources, especially when working with locks.

Submission

Submit the following file(s) by creating an ex17-submit tag in your exercise repository before the assignment deadline. The file(s) should be located in the exact directory listed below, including capitalization:

  • ex17/ex17.cc
  • ex17/SimpleQueue.h
  • ex17/SimpleQueue.cc

All other files you wish to submit must be in the ex17 directory – we will grab everything in the directory that exists in the ex17-submit tag.


Your code must:

  • Compile without errors or warnings on CSE Linux machines (lab workstations, attu, or CSE home VM).
  • Have no runtime errors, memory leaks, or memory errors (g++ and valgrind).
  • Be contained in the files listed above with the provided Makefile compiling your code successfully.
  • Have a comment at the top of your .cc files with your name(s) and CSE or UW email address(es).
  • Be pretty: the formatting, modularization, variable and function names, commenting, and so on should be consistent with class style guidelines. Additionally, the linter shouldn't have any complaints about your code (cpplint.py).
  • Be robust: your code should deal with hard-to-handle/edge cases and bogus user input (if there are any) gracefully.