Please complete the required studio exercises listed below, along with any of the (optional) enrichment exercises that interest you.
As you work through the exercises, please record your answers in a file, and upon completion please e-mail your answers to the firstname.lastname@example.org course e-mail account with Advanced Thread Management Studio in the subject line.
Please make sure that the name of each person who worked on these exercises is listed in the first answer, and that you number your answers so they are easy for us to match up with the appropriate exercise.
In the main C++ source code file for the project (which should be named something like
thread_management.cpp) please modify the main function signature so that it
looks like the standard (i.e., portable between Windows and Linux) main function entry point
int main (int, char * )
interrupt_flag class and an
interruptible_thread class according to
Listing 9.9 of [Williams], and in your main function write a simple test with two threads
that validates and demonstrates the use of those features (see section 9.2.2 of [Williams] for code
examples that may be useful in doing that).
As the answer to this exercise, please show your code and describe how you tested that feature.
unsigned int n, computes the nth Fibonacci number, and returns its value. Use a an atomic
interruptible_threadclass to make that computation interruptible (using the same strategy as in the previous exercise, with the computation checking for interruption after each successive Fibonacci number it computes, (and terminating if so).
Integrate that function within your code from the previous exercise so that the main function (1) launches
std::packaged_task with the Fibonacci number computation in several threads (each with a
different Fibonacci number - some larger and some smaller), (2) repeatedly (i.e., in a loop) does rapid timed
waiting on each of their futures (using a small wait time like a millisecond for each wait) until the first
one completes (based on the status of the future becoming ready as in section 9.2.5 of [Williams]),
(3) interrupts the other computations' threads and joins with each of them that is
still joinable, and then gets and prints out the value of the future that was seen to have completed first.
Build and run your program, and as the answer to this exercise please show your code and the output your program produced.
Implement timed waiting on a
std::condition_variable as in Listing 9.11 of [Williams], and
combine that mechanism with an additional interruption point and the timed waiting on fugures, to wait
to be signaled by the main function's thread. The event loop function should
repeatedly (until interrupted) perform very short timed waits on the condition variable and on the futures of
any Fibonacci number computations that are currently running, and in between each of them should check
whether or not it has been interrupted.
If at any point the event loop discovers that it has been interrupted, it should interrupt each currently running Fibonacci number computation, then once all have been interrupted join with each of them, and then exit.
After waiting on the condition variable, the event loop should check an atomic flag indicating whether it needs to launch a new Fibonacci number computation and if that flag is set then launch a new Fibonacci number computation using the value of another atomic unsigned integer variable (that the main function can assign before it sets the new computation flag) and clear the new computation flag.
Note that in theory this approach has a race for the value of the unsigned integer variable which could cause some requested computations not to be launched. However, we'll ignore that for this exercise, on the assumption that a person (rather than another computer program) will be providing the input that causes each computation to be launched and (1) the input speed will be slow compared to the speed of the event loop (so that the race is unlikely to happen in practice), and (2) if a computation were omitted the person could simply retry launching it.
After waiting for each future the event loop should test whether or not the future is ready (as in section 9.2.5 of [Williams]) and if it is should (1) get the value from the future and print it out, (2) remove the future from the list of futures on which it was waiting, and (3) remove the interruptible thread for that computation from the list of computations on which it was waiting.
Have the main function launch the event loop thread, and then interact with the user by repeatedly prompting (via the standard output stream) for and then reading in (via the standard input stream) a C++ stype string that is either equivalent to "stop" or is an unsigned integer value (all decimal digits) - the program should ignore any other strings and should simply re-prompt for a new input if one is seen. When the program sees an unsigned integer value it should assign it to the atomic unsigned integer variable that it uses to pass values to the event loop, and set the atomic new computation flag, and signal the condition variable on which the event loop may be waiting. When the program sees "stop" it should interrupt the event loop thread (which will then interrupt any currently running Fibonacci number computations) and then join with the event loop thread.
Build and run your program, and as the answer to this exercise please show your code and a trace of the output and input (cut and paste from the terminal window) for an illustrative run of your progtram.
std::condition_variable_anymechanism shown in Listing 9.12 of [Williams], and use it in place of timed waiting on a
std::condition_variable. Note that this may require signaling the condition variable, from each Fibonacci computation when it completes, to avoid deadlock and/or unnecessary delays in when the result of a computation is printed.
Build and run your program, and as the answer to this exercise (1) show your code and (2) describe any interesting features of the implementation, or of the program's behavior using it, which you observed.
thread_localtermination flag (and a simple strategy of checking for termination after each element it looks at) so that all threads search for the same value, and once a thread finds the value that thread sets all of the other threads' termination flags which they each detect and call off their searches.
As the answer to this exercise please describe how you modified your solution from the previous studio, and show your code.