Machine Problem 5: Parallel Make

CS 241


Introduction

More and more programs today are being programmed as multithreaded applications. The goal of this MP is to give you more practice writing multithreaded applications and to expose common pitfalls that occur while designing a program to work in a parallel manner. Additionally, you will need to make use of synchronization primitives to protect memory shared amongst the threads.

You are tasked with writing an application which will imitate the common make utility. We have provided a parse function which will list dependencies and commands to run for different key values. You will need to use this parse function to store the work that needs to be done. Then using a fixed pool of threads, you will make sure that all commands are executed after their dependencies. When there is less work to be done than there are threads, the remaining idle threads should sleep.

TASK

[Part 1]

The main thread should begin by parsing the input file. The input file will always be of the form (specified in BNF):

<Makefile> ::= <job> | <job> <Makefile>
<job> ::= <key> ":" <depend-list> <cmd-list>
<depend-list> ::= " " <key> <depend-list> | <newline>
<cmd-list> ::= <tab> <cmd> <newline> <cmd-list> | <tab> <cmd> <newline>
<key> ::= <a-z,A-Z,0-9> <key> | <a-z,A-Z,0-9>
<cmd> ::= <Valid Unix cmd>
For example:
job1: job2 job3
    commandtoberun withargs
    commandtoberun2 withargs
job2:
    othercommand
job3:
    finalcommand
If you are unfamiliar with the syntax do not be afraid. We have provided you with a parsing function.

When invoking the parser you will have to provide several "call-back" functions. This means you will have to pass a function pointer of the type defined in parser.h. These call-back functions will provide the parsed strings as arguments which you should then store in your own data structure. Those curious of the implementation can view the source in parser.c although this is not necessary for a functioning MP.

It is important to note a few differences from the standard make utility here. The key values do not represent files like in make. This means that all commands should always be executed. There is no need to do any kind of timestamp checks like make does.

[Part 2]

The main thread of the program will read the number of worker threads to be placed in the thread pool from the command line. The argument will use the following syntax.

%>./parmake -j# makefilename
After the input file has been completely parsed, the worker threads should be spawned. Each worker thread will be in charge of processing jobs. This consists of determining whether its dependencies have been fulfilled and then executing any associated commands. There are several important concurrency requirements:

The worker threads should continue processing jobs until all work has been completed. At this point all worker threads should exit as well as the main thread.

NOTES

COMPILING AND RUNNING MP5

To compile, run the following commands from a command prompt on a Linux machine:

%> make clean
%> make

To run the executable,

%> ./parmake <-j#> <file>

Lab Report

In your lab report, please include answers to:

Grading, Submission, and Other Details

Please fully read cs241.html for more details on grading, submission, and other topics that are shared between all MPs in CS 241.