From 67492e13f042521fade3ac21dd213c53c19455c4 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Tue, 4 Jun 2002 16:34:24 +0000 Subject: [PATCH] Modified client observer notification process to: a) use an ObserverList in SAFE_IN_ORDER_NOTIFY mode which circumnavigates any funny business like observers being added or removed while we're notifying the observers. b) require that the clients be notified on the main client thread (generally the AWT thread) by using the invoker; we had a problem previously where clientDidLogoff() was being called by the communicator's writer thread which caused all hell to break loose because everything outside the client networking layer depends on running on the same thread. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1422 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../threerings/presents/client/Client.java | 135 +++++++++++++----- 1 file changed, 98 insertions(+), 37 deletions(-) diff --git a/src/java/com/threerings/presents/client/Client.java b/src/java/com/threerings/presents/client/Client.java index 48ea8f54f..b3206900f 100644 --- a/src/java/com/threerings/presents/client/Client.java +++ b/src/java/com/threerings/presents/client/Client.java @@ -1,11 +1,12 @@ // -// $Id: Client.java,v 1.26 2002/05/28 22:54:43 mdb Exp $ +// $Id: Client.java,v 1.27 2002/06/04 16:34:24 mdb Exp $ package com.threerings.presents.client; import java.util.ArrayList; import java.util.List; +import com.samskivert.util.ObserverList; import com.samskivert.util.ResultListener; import com.threerings.presents.Log; @@ -321,48 +322,42 @@ public class Client boolean notifyObservers (int code, Exception cause) { - boolean rejected = false; - synchronized (_observers) { - for (int i = 0; i < _observers.size(); i++) { - SessionObserver obs = (SessionObserver)_observers.get(i); - switch (code) { - case CLIENT_DID_LOGON: - obs.clientDidLogon(this); - break; - case CLIENT_FAILED_TO_LOGON: - if (obs instanceof ClientObserver) { - ((ClientObserver)obs).clientFailedToLogon(this, cause); - } - break; - case CLIENT_CONNECTION_FAILED: - if (obs instanceof ClientObserver) { - ((ClientObserver)obs).clientConnectionFailed( - this, cause); - } - break; - case CLIENT_WILL_LOGOFF: - if (obs instanceof ClientObserver) { - if (!((ClientObserver)obs).clientWillLogoff(this)) { - rejected = true; - } - } - break; - case CLIENT_DID_LOGOFF: - obs.clientDidLogoff(this); - break; - default: - throw new RuntimeException("Invalid code supplied to " + - "notifyObservers: " + code); + final Notifier noty = new Notifier(code, cause); + Runnable unit = new Runnable() { + public void run () { + synchronized (_observers) { + _observers.apply(noty); } } + }; + + // we need to run immediately if this is WILL_LOGOFF + if (code == CLIENT_WILL_LOGOFF) { + unit.run(); + return noty.getRejected(); + + } else { + // otherwise we can queue this notification up with our + // invoker and ensure that it's run on the proper thread + _invoker.invokeLater(unit); + return false; } - return rejected; } synchronized void communicatorDidExit () { - // clear out our communicator reference - _comm = null; + // we know that prior to the call to this method, the observers + // were notified with CLIENT_DID_LOGOFF; that may not have been + // invoked yet, so we don't want to clear out our communicator + // reference immediately; instead we queue up an invoker unit to + // do so to ensure that it won't happen until CLIENT_DID_LOGOFF + // was dispatched + _invoker.invokeLater(new Runnable() { + public void run () { + // clear out our communicator reference + _comm = null; + } + }); } /** @@ -411,6 +406,71 @@ public class Client } } + /** + * Used to notify client observers of events. + */ + protected class Notifier + implements ObserverList.ObserverOp + { + public Notifier (int code, Exception cause) + { + _code = code; + _cause = cause; + _rejected = false; + } + + public boolean getRejected () + { + return _rejected; + } + + public boolean apply (Object observer) + { + SessionObserver obs = (SessionObserver)observer; + switch (_code) { + case CLIENT_DID_LOGON: + obs.clientDidLogon(Client.this); + break; + + case CLIENT_FAILED_TO_LOGON: + if (obs instanceof ClientObserver) { + ((ClientObserver)obs).clientFailedToLogon( + Client.this, _cause); + } + break; + + case CLIENT_CONNECTION_FAILED: + if (obs instanceof ClientObserver) { + ((ClientObserver)obs).clientConnectionFailed( + Client.this, _cause); + } + break; + + case CLIENT_WILL_LOGOFF: + if (obs instanceof ClientObserver) { + if (!((ClientObserver)obs).clientWillLogoff(Client.this)) { + _rejected = true; + } + } + break; + + case CLIENT_DID_LOGOFF: + obs.clientDidLogoff(Client.this); + break; + + default: + throw new RuntimeException("Invalid code supplied to " + + "notifyObservers: " + _code); + } + + return true; + } + + protected int _code; + protected Exception _cause; + protected boolean _rejected; + } + /** The credentials we used to authenticate with the server. */ protected Credentials _creds; @@ -431,7 +491,8 @@ public class Client protected int _port; /** Our list of client observers. */ - protected List _observers = new ArrayList(); + protected ObserverList _observers = + new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY); /** The entity that manages our network communications. */ protected Communicator _comm;