Homework Solution

My solution to the homework assignement on nondeterministic merge in PN is given in the NondeterministicMerge actor. Hopefully the documentation in the code is sufficient to understand it.

  1. What happens in your actor code when your model is executing the stop button is pushed on the toolbar?
    Solution: In my implementation, the lifecycle of the contained actors is handled by the PNDirector in its usual way, which we need to be concerned about. Internally, when the stop button is pushed, any threads that are blocked on a put() or get() call on a port will get a TerminateProcessException thrown (this is documented in PNQueueReceiver), which will cause the put() or get() to fail. The thread will exit the fire() method with an exception, which will cause the enclosing ProcessThread to exit.
  2. Create a deadlock in your model. What happens to your code?
    Solution: The solution here depends on what is meant by deadlock. One possible interpretation is that all actors are blocked on read, including the actors inside the NondeterministicMerge composite. In this case, the deadlock is handled by the PNDirector in its usual way. It determines that all threads under its control are blocked on reads and terminates its execution. The following model works this way:
  3. Create a feedback path from the output of your nondeterminstic merge back to one of its inputs. Discuss what happens when a stream is presented at the other input.
    Solution: The following model does what is requested:
    One possible result of running the model is shown below: Solution: The following model does what is requested:
    Notice that this model is not behaving as one would expect if the Parks strategy were properly adapted to handle the nondeterministic merge. In particular, it is possible to execute this model in bounded memory by using blocking writes to prevent more than a finite number of executions of the Ramp actor. However, the current implementation does not do this. In fact, running the model indefinitely will eventually result in overflowing the buffers.

There are several other possible solutions:

  1. One solution creates a thread for input channel that repeatedly reads the input channel and writes to the output. Unlike my solution, that thread is not under the control of a PNDirector, and so extra care must be taken to ensure that the thread terminates properly when the actor thread terminates. Also, extra care must be taken to ensure that the actor thread does not consume resources unnecessarily by busy waiting (which it will do if, for example, its fire() method is empty). Getting this solution right in each of the cases is tricky. In particular, the PNDirector is written to assume that only one thread will ever call get() on the ports of the actor. When that thread blocks, it assumes that the actor thread is blocked, but in this case, there are two more threads, and it may be that neither is blocked. This solution violates many of the assumptions of the PNDirector design, and hence is very hard to get working completely.
  2. Another solution, due to Slobodan Matic, is to create a thread for each input port that reads exactly one input token and then dies. In the initialize() method of the actor, it creates one instance of this thread for each input port. In the fire() method, it checks to see whether each thread is alive, and if either is not, it produces the output. A down side of this solution is that the fire() method busy waits for the threads to die. This could be fixed by having the fire() method of the actor wait for a notification from either port thread, which would issue the notification before dying. This still suffers the problem that multiple threads call get(), which can confuse the director's deadlock detection mechanism.