From 3be1d56ae701a5de9b9b6eb43da83c62f63c48e7 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Wed, 10 Jul 2002 01:23:45 +0000 Subject: [PATCH] Cleaned up connection handling code so that it deals more robustly and gracefully with failures; also no longer attempt to send messages to a connection that has already been closed. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1565 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../presents/server/net/Connection.java | 71 +++++++++++++++---- .../server/net/ConnectionManager.java | 21 +++++- 2 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/java/com/threerings/presents/server/net/Connection.java b/src/java/com/threerings/presents/server/net/Connection.java index ab10c6587..70843026e 100644 --- a/src/java/com/threerings/presents/server/net/Connection.java +++ b/src/java/com/threerings/presents/server/net/Connection.java @@ -1,5 +1,5 @@ // -// $Id: Connection.java,v 1.6 2001/10/11 04:07:53 mdb Exp $ +// $Id: Connection.java,v 1.7 2002/07/10 01:23:45 mdb Exp $ package com.threerings.presents.server.net; @@ -81,6 +81,14 @@ public abstract class Connection implements NetEventHandler return _socket; } + /** + * Returns true if this connection is closed. + */ + public boolean isClosed () + { + return (_socket == null); + } + /** * Closes this connection and unregisters it from the connection * manager. This should only be called from the conmgr thread. @@ -88,8 +96,9 @@ public abstract class Connection implements NetEventHandler public void close () { // we shouldn't be closed twice - if (_socket == null) { - Log.warning("Attempted to re-close connection."); + if (isClosed()) { + Log.warning("Attempted to re-close connection " + this + "."); + Thread.dumpStack(); return; } @@ -97,15 +106,48 @@ public abstract class Connection implements NetEventHandler _cmgr.connectionClosed(this); // close our socket - try { - _socket.close(); - } catch (IOException ioe) { - Log.warning("Error closing connection [conn=" + this + - ", error=" + ioe + "]."); + closeSocket(); + } + + /** + * Called when there is a failure reading or writing on this + * connection. We notify the connection manager and close ourselves + * down. + */ + public void handleFailure (IOException ioe) + { + // if we're already closed, then something is seriously funny + if (isClosed()) { + Log.warning("Failure reported on closed connection " + this + "."); + Thread.dumpStack(); + return; } - // clear out our socket reference to prevent repeat closings - _socket = null; + // let the connection manager know we're hosed + _cmgr.connectionFailed(this, ioe); + + // and close our socket + closeSocket(); + } + + /** + * Closes the socket associated with this connection. This happens + * when we receive EOF, are requested to close down or when our + * connection fails. + */ + protected void closeSocket () + { + if (_socket != null) { + try { + _socket.close(); + } catch (IOException ioe) { + Log.warning("Error closing connection [conn=" + this + + ", error=" + ioe + "]."); + } + + // clear out our socket reference to prevent repeat closings + _socket = null; + } } /** @@ -122,15 +164,14 @@ public abstract class Connection implements NetEventHandler } } catch (EOFException eofe) { - // let the connection manager know that we done went away - _cmgr.connectionClosed(this); + // close down the socket gracefully + close(); } catch (IOException ioe) { Log.warning("Error reading message from socket " + "[socket=" + _socket + ", error=" + ioe + "]."); - Log.logStackTrace(ioe); - // let the connection manager know that something when awry - _cmgr.connectionFailed(this, ioe); + // deal with the failure + handleFailure(ioe); } } diff --git a/src/java/com/threerings/presents/server/net/ConnectionManager.java b/src/java/com/threerings/presents/server/net/ConnectionManager.java index 6fce1fd2b..2005fbeed 100644 --- a/src/java/com/threerings/presents/server/net/ConnectionManager.java +++ b/src/java/com/threerings/presents/server/net/ConnectionManager.java @@ -1,5 +1,5 @@ // -// $Id: ConnectionManager.java,v 1.18 2002/03/28 22:32:32 mdb Exp $ +// $Id: ConnectionManager.java,v 1.19 2002/07/10 01:23:45 mdb Exp $ package com.threerings.presents.server.net; @@ -173,13 +173,27 @@ public class ConnectionManager extends LoopingThread // close any connections that have been queued up to die Connection dconn; while ((dconn = (Connection)_deathq.getNonBlocking()) != null) { - dconn.close(); + // it's possible that we caught an EOF trying to read from + // this connection even after it was queued up for death, so + // let's avoid trying to close it twice + if (!dconn.isClosed()) { + dconn.close(); + } } // send any messages that are waiting on the outgoing queue Tuple tup; while ((tup = (Tuple)_outq.getNonBlocking()) != null) { Connection conn = (Connection)tup.left; + // if the connection to which this message is destined is + // closed, drop the message and move along quietly; this is + // perfectly legal, a user can logoff whenever they like, even + // if we still have things to tell them; such is life in a + // fully asynchronous distributed system + if (conn.isClosed()) { + continue; + } + DownstreamMessage outmsg = (DownstreamMessage)tup.right; try { // first flatten the message (and frame it) @@ -188,7 +202,8 @@ public class ConnectionManager extends LoopingThread _framer.writeFrameAndReset(conn.getOutputStream()); } catch (IOException ioe) { - connectionFailed(conn, ioe); + // instruct the connection to deal with its failure + conn.handleFailure(ioe); } }