CSE 333 Exercise 17
Due: Monday, March 9 by 11 AM
Rating: 2 (note)
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:
- Start a producer of Mulgikapsad,
- Start a producer of kama,
- 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 functionsSimpleQueue.h, the header file forSimpleQueue, a linked list queue that is used as the producer-consumer bufferSimpleQueue.cc, the implementation ofSimpleQueueMakefile, a makefile that will build theex17program
SimpleQueue.h,
SimpleQueue.cc, and ex17.cc
so you should not modify the Makefile.
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.ccex17/SimpleQueue.hex17/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++andvalgrind). - Be contained in the files listed above with the provided Makefile compiling your code successfully.
- Have a comment at the top of your
.ccfiles 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.