Directorclass through which threads from the pool are coordinated based on the Half-Sync/Half-Async pattern, the Leader/Followers pattern, or a combination of the two.
If you are working in a different team for this lab than in a previous lab, you are allowed to reuse code from any of your earlier lab solutions though if you do that you should please add comments acknowledging contributions from anyone who is not on your current team (and noting in which previous lab their contributions were made), to the code you submit.
Make separate copies of the (source and header) code files from lab 1 that you are using for this assignment, and put them into the directory where your new project keeps its own source files, before adding them to your lab 2 project. Not only can this (1) avoid confusing Visual Studio (with regard to which files to build, where to find the object files from them, etc.), but it also makes it easier to ensure (2) you have all the files when packaging up your solution to submit it, and makes it easier (3) to revert to a previously stable code base if a change you are trying doesn't pan out well.
In this assignment, you will again work with input files in similar formats as in the previous lab 1 assignment, but instead of working with a single configuration file will work with a script file that contains the names of a sequence of configuration files, each of which defines a scene fragment as in the previous labs.
For example, the first part of Act II from William Shakespeare's
"Hamlet, Prince of Denmark", (text obtained from the Literature Page
web site at
can be represented within a script file
that contains an ordered sequence of
[scene] directives, each of which is followed by the name(s)
of one or more configuration files
scene fragments that in the play are delimited by character
entrances and/or exits:
(we'll implement a simple version of character entrances and exits in this lab, and then will further extend this feature in lab 3).
As in the previous labs, each scene fragment's configuration file gives the name of each character in the scene fragment along with their corresponding part file (e.g., Polonius_hamlet_ii_1a.txt and Reynaldo_hamlet_ii_1a.txt in hamlet_ii_1a_config.txt, Polonius_hamlet_ii_1b.txt and Ophelia_hamlet_ii_1b.txt in hamlet_ii_1b_config.txt, and King_hamlet_ii_2a.txt and Queen_hamlet_ii_2a.txt and Rosencrantz_hamlet_ii_2a.txt and Guildenstern_hamlet_ii_2a.txt in hamlet_ii_2a_config.txt).
Note that for this lab the names of the scenes have moved from the
individual configuration files into the script file's
To complete this assignment please perform the following tasks:
countermember variable to something like
scene_fragment_countermember variable for the current scene fragment that is in progress, which the
Playclass constructor should initialize to 0 or 1 depending on how you handle increments and decrements to it in the
exitmethods respectively (see below);
recitemethod so that in addition to an iterator over a container of structured lines, it also takes a numeric argument giving the number of the current scene fragment in which the lines are to be recited.
Also update the
recite method's implementation so that it compares the values of both the
scene_fragment_counter member variables, to the line
number of the structured line to which the passed iterator refers and the passed scene fragment number,
scene_fragment_counter member variable is less than the passed scene
fragment number, or is equal but the
line_counter member variable is less
than the line number in the structured line referenced by the passed iterator, the
recite method should repeatedly wait on a condition variable, until the
scene_fragment_counter member variables reach the values given in the the corresponding passed
scene_fragment_counter member variable equals the passed scene
fragment number and the
line_counter member variable equals the structured line's number,
recite method should print out the line
cout, the standard output stream), increment the iterator, notify all
other threads waiting on the condition variable, and return.
To avoid potential deadlocks and other problems if a badly formed script fragment is provided or
somehow the program loses track of the count, if the
variable is ever greater than the passed scene
fragment number, or is equal but the
line_counter member variable is greater
than the line number in the structured line referenced by the passed iterator, the
method should (1) write a line containing an error message to the standard error stream,
(2) increment the iterator (but not the line counter), (3) notify all other threads waiting on the
condition variable, and (4) return.
on_stagemember variable for the number of characters currently on stage in the current scene fragment, which the
Playclass constructor should initialize to 0;
entermethod that takes a numeric argument for the scene fragment that the character is attempting to enter: if the passed value is less than the
scene_fragment_countermember variable, the method should simply fail (by returning a non-zero error code, or throwing an exception, etc.); if the passed value is equal to the
scene_fragment_countermember variable, the method should increment the
on_stagemember variable and return; if the passed value is greater than the
scene_fragment_countermember variable, the method should wait on a condition variable until they are equal, and then increment the
on_stagemember variable and return.
exitmethod that: (1) if the
on_stagemember variable is greater than 1 simply decrements the
on_stagemember variable; or (2) if the
on_stagemember variable is less than 1 throws an exception or returns a non-zero error code; or (3) if the
on_stagemember variable is exactly 1 then (a) decrements the
on_stagemember variable, (b) increments the
scene_fragment_countermember variable, (c) if the iterator member variable is not already past the end of the container of scene titles prints out the string the iterator member variable currently references if that string is non-empty and then (whether or not the string was empty) increments the iterator member variable, and (d) signals the condition variable used by the
Playclass as needed to prevent significant race conditions while achieving high concurrency.
Whether or not the
exit methods use the same condition variable as
recite method is up to you to choose. Design issues may include spurious wakeups vs.
any relationships among the condition expressions that you might want to consider, as well as complexity
of the implementation. Whatever you decide, in your project report please describe the issues you considered,
how you chose to design this, and why you chose your particular design.
Directorclass as follows:
The constructor should open the script file in an input file stream and read through it (if the script file cannot
be opened the constructor should throw an exception). Whenever a new scene appears in the script (indicated by a
line that begins with
[scene] followed by the scene's title), the constructor should store scene's title
(i.e., the rest of the line after the
[scene] token) into a C++ style string and push that string back
into a container member variable that holds the titles of the scenes (each of which will be printed out by the
Play class object whenever the scene changes.
Lines of the script file that do not start with a
[scene] token should be interpreted as containing
names of configuration files for the various fragments of the script. The constructor should attempt to open each
configuration file, and for each configuration file that it can open should (1) count how many part definition lines
each configuration file contains, (2) keep track of the maximum sum of the numbers of part configuration lines that
appear in any two consecutive configuration files, and (3) when it reaches a configuration file line that immediately
follows another configuration file line (i.e., does not immediately follow a new scene line that begins with the
[scene] token) should push an empty string into container of scene titles.
After it finishes reading the script file, the constructor should dynamically (i.e, using the
operator) allocate a
Play object (passing a reference to the container of scene title strings into the
Play constructor), and store a
shared_ptr (or other appropriate C++ smart pointer) to that
Play object in a member
variable so that when the
Director is destroyed so is the
Director class constructor should then take the maximum of the passed unsigned integer
value and the maximum number of part lines in two consecutive script fragments, and should dynamically allocate
(i.e, again using the
new operator) that many
Player objects (passing each one a reference
Play object, and should push back a
shared_ptr to each one into a container member variable of the
cuemethod that can repeatedly hand off the name of a character and the name of a part definition file for that character, and a scene fragment number, to a
Player, and with the information they contain run the
actmethods to perform that part within the play.
How you design and implement this method (along with any changes to any other parts of your lab 2 solution in order to accommodate your design) is up to you, but there are at least two reasonable approaches you could take, according the the Half-Sync/Half-Async (HS/HA) vs. Leader/Followers (L/F) patterns we have discussed in class.
A key idea for either variation (which may not be strictly necessary for the HS/HA variation even if it is
potentially helpful, but probably it or something like it is in fact necessary for the L/F version) is to
have the constructor build an explicit representation
of the script as it parses the script file, perhaps with a
script struct that has a container of
shared_ptrs to dynamically allocated scene
fragment structs and an iterator over that
container, and with each
fragment struct having a container of
shared_ptrs to dynamically
part structs (each containing a character name and an iterator over the part
definition file) and an iterator over that container.
You could even have an intermediate
scene struct for an entire scene within this nested
data structure (e.g., pointed to from within the
struct's container of smart pointers, and itself containing
structs), but that may not be necessary unless it helps simplify other parts of your design.
In one design approach, which follows the HS/HA pattern, the
cue method could be designed to be called
once, and immediately spawn its own thread that then either traverses the data structure described above or
simply re-processes the script file and configuration files, and as it reaches each scene fragment
takes each part definition line, recruits an available
Player from its container,
and calls the
enter method to hand off the information. The design of the
Player's enter method is up to you as well, but one reasonable way to do this for the HS/HA approach
is to make each
Player an active object with its own input queue, and then have the
object's internal thread repeatedly pull information from its queue and process via its
act methods. This HS/HA + Active Object approach potentially is more straightforward to
design and implement than the L/F variation, even though it involves the use of a locked queue data structure
with condition variables to avoid busy waiting, etc.
In another design approach, which follows the L/F pattern, the
cue method could be called by each new
leader when elected, and each time the
cue method is called it would check and
update iterators at each level so that the sequence of successive calls to the
cue method would
iterate through the entire play, with the leader taking each new part and going off to perform it, while a new
leader is elected to take on the next part. In that case, an appropriately synchronized method for leader
election should be added to the
Player (or other appropriate) class and the main thread in each
Player should repeatedly call that method until it is time for the thread to terminate.
Note that one wrinkle you may need to address in the L/F variation is when the main thread is allowed to terminate
(with the HS/HA variation the main thread presumably would be running the
Director and so its
appropriate termination point and the cleanup sequence afterward would be reasonably obvious),
and what consequences that may have on the availability of program resources to the other threads, etc.
For either of these variations (or for any alternative approach you may come up with) a termination
protocol is needed so that after the script file has been completed, the
Player threads all
terminate and the
Player objects and all other resources of the program
are freed after which the entire program ends. For the approach combining the HS/HA and Active Object
patterns, for example, it may be straightforward to use a special termination ACT that is passed to each
Player object's queue, which would cause the
Player's thread to exit, and the
Director could then join with each of those threads (e.g., by calling each
exit method in which the join would occur as in lab 1. For the L/F approach, it may be appropriate
to modify the
Director class so that the leader can detect when there are no more parts to be played,
it can simply end its own thread, let the next leader be elected which then also will terminate, etc.
For this lab assignment, you must carefully document your design decisions and your rationale for them, on this issue, on the other design issues that are raised in this document, and on any other important issues you may encounter while developing your lab solution. For this assignment the project report will be worth 10 percent of the points, and in the final project the project report will be 20 percent of the grade.
Playerclass constructor, member variables, and methods so that they are compatible with the HS/HA or L/F design approach you take per the instructions above. In particular, you should consider whether the following changes are compatible with your approach (and if not should modify them accordingly). Again, please make sure to document your design decisions and rationale for this part as well, in your project report.
Playin which it is performing (and does not store the name of a character or a character part file stream).
readmethod so that each time it is called it first empties the container of structured lines that is stored by the
Playerand then re-populates the container with lines from the character part file stream.
actmethod so that it is compatible with the HS/HA or L/F approach taken in your design. For example if you apply the HS/HA and Active Object patterns, the act method may repeatedly pull from the object's input queue and process each item it is given - alternatively, the act method could be the point where you initiate leader election if your design pursued the L/F variation.
exitmethods so that they are compatible with the appropriate thread pool management semantics for the HS/HA or L/F design approach you chose. In particular, issues of thread construction, thread spawning, and thread join or detach semantics should be considered for these methods.
argv) to identify a script file formatted like partial_hamlet_act_ii_script.txt, and if a second command line argument was given (in
argv) should treat that argument as an unsigned integer giving the minimum number of players to use in performing the play, i.e., by extracting an
unsigned intvalue from it via an
If it is run without a script file name your program should again print out a useful syntax message (before ending gracefully by returning a unique non-zero error code), like the following:
cout << "usage: " << argv << "<script_file_name>" << endl;
Otherwise, the main function should then construct a
Director object using that argument (and if one was
given, the minimum number of players).
What the main function should do next depends on your thread pool concurrency design, as described above. For this issue as well, please document your design decisions and your rationale for them, in your project report.
If a severe error occurs that prevents the program from making progress, an exception should be thrown in the code that encounters the problem, and the main function should catch all exceptions, generate an appropriate error message, make sure all program resources are cleaned up gracefully, and then return a unique non-zero error code for each different kind of failure (if the program completes successfully, it should return 0).
For this assignment you should again ensure that if one or more of the character parts is mis-ordered (i.e., the line numbers are not in ascending order in the file) the program at least doesn't deadlock, and if possible delivers at least the lines with line numbers that are seen in ascending order.
You are strongly encouraged to complete the extra credit portion of the assignment, which in addition to earning you points directly can make it easier to generate (and then evaluate) different test cases from existing sources for plays (e.g., on the web), and also to construct scenarios in which you can drive your program's concurrency architecture into deadlock and other pathological cases to observe the conditions under which it can operate successfully in constrast to those cases where it cannot (understandably so - note that your program is NOT required to handle those pathalogical cases successfully, but rather exploring them can give insight into your program's ability to tolerate the relevant issues up to a certain point, if not beyond it).
For example, if given the input file hamlet_act_ii_scene_1.txt, the program should produce a corresponding script file like partial_hamlet_act_ii_scene_1_script.txt, scene fragment configuration files like hamlet_ii_1a_config.txt and hamlet_ii_1b_config.txt, and part files like Polonius_hamlet_ii_1a.txt and Reynaldo_hamlet_ii_1a.txt and Polonius_hamlet_ii_1b.txt and Ophelia_hamlet_ii_1b.txt.
Submit your solution to this part of the extra credit in a separate .zip file (clearly named to indicate it's the extra credit part) that you should attach along with the other parts of the assignment to the e-mail you send when submitting your solution. Also write up your approach to the extra credit part, give instructions for how to unpack, build, and use it, and document how you used it in your testing of the assignment, in the main ReadMe.txt file for your assignment (instead of creating a separate ReadMe.txt file for the extra credit part).
Directorclass constructor so that if a third argument (that must be exactly the C-style string
"-override"if given) is passed from the program's command line to the main function, a boolean flag is set to true and passed to the
Directorclass constructor; otherwise the passed boolean value should be false. If the boolean value is true, instead of looking up how many players are needed in consecutive scene fragments of the script file, the
Directorclass constructor should simply create and use exactly the number of players that was passed to it. Build and run the program using the
"-override"argument following different numbers of players to use, especially greater and smaller numbers of players, and in your project report please describe the trials you ran, and what behaviors you saw in those trials - in particular, please indicate whether you were ever able to get the program to deadlock when you did that, and why do you think it did or did not deadlock?
The first section of your ReadMe.txt file should include:
The second section of your ReadMe.txt file should provide detailed instructions for how to:
The third section of your ReadMe.txt file should provide a reasonably detailed description of how you evaluated your solution, including the kinds of configuration and character part files you used and their formats (including well formed and badly formed content, misordered lines, etc. to test how your program handled those variations), and any other scenarios that you tested that you consider important.
firstname.lastname@example.org a single .zip file attached containing:
#if ! defined (MY_FILE_H)
// body of the header file
#endif /* defined MY_FILE_H */
new) and/or write exception safe code (e.g., using the RAII idiom) - please don't let an exception escape main uncaught, though.
Posted 11:15am Monday October 5, 2015 by Chris Gill.
Changes since original posting: