org.jboss.remoting.transport.multiplex
Class MultiplexingManager.ShutdownManager
java.lang.Object
org.jboss.remoting.transport.multiplex.MultiplexingManager.ShutdownManager
- Enclosing class:
- MultiplexingManager
protected class MultiplexingManager.ShutdownManager
- extends java.lang.Object
The motivation behind this class is to prevent the following problem. Suppose MultiplexingManager A
is connected to MultiplexingManager B, and A decides to shut down. Suppose A shuts down before B knows A
is shutting down, and suppose a VirtualSocket C starts up, finds B, and attaches itself to B. Then C
will have "died a-borning," in the words of Tom Paxton. We need a handshake protocol to ensure a
proper shutdown process. In the following, let A be the local MultiplexingManager and let B be its
remote peer.
There are two forms of synchronization in ShutdownManager. incrementReferences() and decrementReferences()
maintain the reference count to its MultiplexingManager, and of course the changes to variable
referenceCount have to be synchronized. Parallel to incrementReferences() and decrementReferences() are
the pair of methods reseverManager() and unreserveManager(), which are similar but intended for holding a
MultiplexingManger for more temporary applications. See, for example, getaManagerByRemoteAddress().
There is also a need for distributed synchronization. When decrementReferences() on A decrements the
referenceCount to 0, it indicates to B the desire of A to shut down. Since all of the virtual sockets
on A are connected to virtual sockets on B, normally the request would be honored. However, if a new virtual
socket attaches itself to B, then it would have > 0 clients, and it would refuse the request to shut down.
In any case, the request is made through Protocol.requestManagerShutdown(), which results in a call to
ShutdownManager.respondToShutdownRequest() on B, which is synchronized since it reads the
readyToShutdown variable, which is modified by decrementReferences(). Here lies the danger of
distributed deadlock. If decrementReferences() on A and B both start executing at about the same time, they
would both be waiting for a response from respondToShutdownRequest(). But each respondToShutdownRequest()
would be locked out because each decrementReferences() is blocked on i/o.
The solution is to put the i/o operation in a separate thread, ShutdownRequestThread, and have decrementReferences()
enter a wait state, allowing respondToShutdownRequest() to execute. So, on each end respondToShutdownRequest()
will return an answer ("go ahead and shut down", in particular), and each ShutdownRequestThread.run() will wake up
the waiting decrementReferences(), which will then run to completion.
Note also that while decrementReferences() is waiting, incrementReferences() can run. However, before it waits,
decrementReferences() sets the shutdownRequestInProgress flag, and if incrementReferences() finds the flag set, it
will also enter a wait state and will take no action until the outstanding shutdown request is accepted or
rejected.
Another issue is what to do if MultiplexingManager B responds negatively to A's request to shut down, not
because it has a new client but simply because some of its virtual sockets just haven't gotten around to
closing yet. When B's referenceCount finally goes to 0, it will send a shutdown request to A, and if A's
referenceCount is still 0, B will shut down. But what about B? If decrementReferences() gets a negative
response, it will start up a ShutdownMonitorThread, which, as long as readyToShutdown remains true, will
periodically check to see if remoteShutdown has been set to true. If it has, ShutdownMonitorThread
initiates the shut down of its enclosing MultiplexingManager.
reserveManager() interacts with decrementReferences() by preventing decrementReferences() from committing to
shutting down. If reserveManager() runs first, it sets the flag reserved to true, which causes
decrementReferences() to return without checking for referenceCount == 0. If decrementReferences() runs
first and finds referenceCount == 0 and gets a positve response from the remote manager, then reserveManager()
will throw an IOException. But if decrementReferences() gets a negative response, it will start up a
ShutdownMonitorThread, which runs as long as the flag readyToShutdown is true. But reserveManager() will
set readyToShutdown to false, ending the ShutdownMonitorThread. When unreserveManager() eventually runs
and sees referenceCount == 0, it will increment referenceCount and call decrementReferences(), allowing the
shutdown attempt to proceed anew. Note that when incrementReferences() runs successfully, it sets the flag
reserved to false, since incrementing referenceCount will also keep the MultiplexingManager alive.
Methods inherited from class java.lang.Object |
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
MultiplexingManager.ShutdownManager
protected MultiplexingManager.ShutdownManager()
reserveManager
public void reserveManager()
throws java.io.IOException
- Throws:
java.io.IOException
unreserveManager
public void unreserveManager()
incrementReferences
public void incrementReferences()
throws java.io.IOException
- Throws:
java.io.IOException
decrementReferences
public void decrementReferences()
respondToShutdownRequest
protected boolean respondToShutdownRequest()
- Returns:
isShutdown
protected boolean isShutdown()
- Returns:
Copyright © 1998-2005 JBoss Inc . All Rights Reserved.