CSE 532S Active Object Studio

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 cse532@seas.wustl.edu course e-mail account with Active Object 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.


    Required Exercises

  1. As the answer to the first exercise, list the names of the people who worked together on this studio.

  2. Open up Visual Studio 2013, make sure your settings are for C++, and create a new project for this studio (for example named something like active_object).

    Copy over your source and header files for the synchronized_list class template from the previous studio into the directory where the new project expects to find them, and add them to the project. Please make sure you make separate copies of the files in the directory where Visual Studio 2013 expects to find source and header files from the project, rather than attempting to add the original Monitor Object studio's files into the Active Object studio's project - not only will you make it easier for Visual Studio 2013 to find things where it expects them (and thus avoid some errors that can crop up otherwise), but you also will have a version of your code that you can look back at (and possibly revert back to) even as you're making changes to the version that you're updating in this studio.

    In the main C++ source code file for the project (which should be named something like active_object.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 for C++: int main (int, char * [])

    In a separate header (and possibly source) file, declare and define an active_object class template parameterized with a data type parameter T, which has a synchronized_list of T as a private member variable, a public default constructor (that default constructs the member variable), a public enqueue method that takes a T and calls its synchronized_list member variable's push_front method, and a public run_service method that calls the synchronized_list member variable's pop_back method, prints out the value it obtained from that call to the standard output stream, and then returns.

    In your main function, declare an instance of your active_object class template (on the stack) parameterized with int, and try calling its enqueue and run_service methods in different orders (some of which may deadlock and some of which may not, due to the synchronization within the synchronized_list member variable) and with different integer values.

    Build and run your program with those methods called in different orders, and as the answer to this exercise, please describe under which conditions the program deadlocked and under which it did not.

  3. Add a private boolean flag member variable (to indicate whether or not the object is active, which the default constructor initializes to false). Make the run_service method private, and modify it so that while the flag member variable is true (if not the method should simply return) it repeatedly (1) calls the synchronized_list member variable's pop_back method, and (2) prints out the value it obtained from that call, to the standard output stream.

    Add a public activate method that checks whether or not the object is already active and if it is not constructs a (local) std::thread object with a pointer to the run_service method and a pointer to the current object, and then detaches the thread. For this studio we'll rely on the main function to call the activate (and later the deactivate) method only once for each active object, though in practice (and optionally as an enrichment exercise today) you should also ensure it is thread safe but doesn't diminish concurrency unnecessarily.

    Modify your main program so that instead of calling the run_service method it calls the activate method, calls the enqueue method repeatedly, sleeps for a sufficient interval (so that the enqueued values all can be popped and printed out), and then returns.

    Please make sure to add any additional synchronization that is needed, but please also think about whether or not the synchronizaton within the synchronized_list is adequate.

    Build and run your program, and as the answer to this exercise please describe how the program's behavior changed, compared to its behavior in the previous exercise.

  4. Modify your implementation of the synchronized_list so that it has a flag to determine whether or not it has been shut down, and update the synchronization logic so that if it has been shut down a thread that would have waited for its condition to become true now simply returns from the method it is in. Add a shut_down method that (1) acquires the lock, (2) sets that flag (3) and then calls notify_all (to wake up all waiting threads) before releasing the lock and returning.

    Add a public deactivate method to your active object that checks whether or not the object is already active and if it is sets the flag member variable to false and then calls the shut_down method of the synchronized_list, before returning.

    In your main function, try repeatedly activating, using, and deactivating the same active object, with suitable sleep intervals to allow the active object to complete its work (in practice, and optionally as an enrichment exercise in today's studio, you would use promises and futures or some other explicit coordination mechanism to ensure a reliable ordering of initiation and completion events across multiple threads). Build and run your program, and as the answer to this exercise please describe the program's behavior in each variation you tried.

  5. Modify the constructor of your active object class template so that it optionally takes a pointer to another object of the same type (defaulted to 0 if no pointer is passed) and stores it in a private member variable (which you should also add).

    Also modify your run_service method so that if the pointer is zero it prints out each value as before, but if the pointer is non-zero it instead enqueues the value in the active object to which it is pointing (unless there is a cycle of pointers among objects, one of them is at the end of the chain and eventually prints out the values).

    In your main function, default construct one active object, and then construct another active object with its address (please also feel free to try chains longer than 2, if you'd like), and enqueue a number of values into the second active object (but not into the first one, since the second one will pass them all to it).

    Build and run your program and as the answer to this exercise (1) show the output your program produced, and (2) describe any differences you saw in how the program behaved in this exercise, compared to in the previous ones.

    Enrichment Exercises (Optional)

  6. Try invoking the activate and deactivate methods of one or more active objects, from the main thread and/or other threads. See if you are able to discover any races or other concurrency hazards involving them, or if not please think about whether any might be possible. Modify the definitions of those methods to avoid those hazards, and as the answer to this exercise please describe what you observed and how you modified those methods.

  7. Instead of relying on sleep in the main thread, add promises and/or futures to your implementation to allow a thread to signal when it has finished all of its work, and is quiescent. Build and run your program, and as the answer to this exercise please describe your design and implementation, and how the program behaved as a result.