Note: Make reasonable assumptions where necessary and clearly state them. Feel free to discuss problems with classmates, but the only written material that you may consult while writing your solutions are the textbook and lecture slides.
You may complete this assignment in teams of up to three students. Plan early -- you don't want to be stuck debugging on the last night with the TAs not around to help.
Problem: Consider an application with one master thread, one producer thread, and four consumer threads.
The application has a central data structure: A 4-element array of doubly-linked lists (you need a head pointer and a tail pointer to represent each doubly-linked list). Each linked list consists of a task queue for one of the four consumer threads. Each element in the linked list stores an "oldvalue" and "newvalue" (in addition to pointers for the next and previous elements).
The producer thread reads an input file (example format here ) that contains a number of entries. Each entry contains the id of the consumer thread that should handle this task, and two other integers ("oldvalue" and "newvalue"). After reading an entry, the producer inserts this task at the tail of the task queue for the corresponding consumer. It then sleeps for a random amount of time between 0 to 1 milli-seconds and then reads the next entry in the input file.
The consumer thread pulls out the head of its task queue (does nothing if the queue is empty) and appends the values of "oldvalue" and "newvalue" to its output file. It then sleeps for a random amount of time between 0 to 4 milli-seconds and checks the task queue again. At the end of the program, the output file (for the input file listed above) should be something like this for consumer 0.
While all of the above is happening, the master thread keeps walking through all the linked lists and incrementing the value of "newvalue" in every element. After walking through every linked list, it sleeps for a random amount of time between 0 to 1 nano-second (in reality, it sleeps longer).
You need to write message-passing and shared-memory versions of this application (or rather, fill in missing pieces in partially completed versions).
First, consider the message-passing version. Here is the message-passing version of the program (it is well documented). It is quite complete except for the message transfers back and forth between the consumer and master thread. You need to understand the message-passing version of the code and introduce whatever is required to effect the above communication. Once you understand the code, it will help you greatly with the shared-memory version of the code. This is a great starting point that explains how to get started running an MPI application. In summary, you need to do the following steps:
After confirming that your message-passing version works correctly and produces the correct output files, examine the Pthread version of the code (written with the shared-memory programming model). This code only has the function for the master thread. You need to provide the code for the producer and consumer threads. Luckily, most of the necessary functions are already provided in the MPI version of the code -- feel free to borrow whatever you need from that code (of course, you will not be borrowing any of the SENDs and RECEIVEs). To run the pthread version, simply do "gcc -pthread pthr.c" and then run "./a.out".
What you need to submit to the TAs (email avishek and vvenkate): your new mpi.c and new pthr.c with the above changes.
The following resources will help you learn the basics of Pthreads/MPI programming.
Resources: