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.
- 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.
- 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:
- 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:
-
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.
-
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.