From 42110dc8da66ec8f0a0a4fddef4e3a3a0e78b6fd Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Sun, 8 Dec 2002 02:18:50 +0000 Subject: [PATCH] Remove invocation receiver mappings from the receivers table when they are unregistered. Flush invocation listener mappings after an expiry period if they were not already removed by virtue of receiving a response. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2040 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../presents/client/InvocationDirector.java | 60 ++++++++++++++++++- .../presents/data/InvocationMarshaller.java | 6 +- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/java/com/threerings/presents/client/InvocationDirector.java b/src/java/com/threerings/presents/client/InvocationDirector.java index 404c64323..e792b545d 100644 --- a/src/java/com/threerings/presents/client/InvocationDirector.java +++ b/src/java/com/threerings/presents/client/InvocationDirector.java @@ -1,5 +1,5 @@ // -// $Id: InvocationDirector.java,v 1.23 2002/10/27 22:24:20 mdb Exp $ +// $Id: InvocationDirector.java,v 1.24 2002/12/08 02:18:50 mdb Exp $ package com.threerings.presents.client; @@ -126,6 +126,14 @@ public class InvocationDirector // if we're logged on, clear out any receiver id mapping if (_clobj != null) { + Registration rreg = (Registration) + _clobj.receivers.get(receiverCode); + if (rreg == null) { + Log.warning("Receiver unregistered for which we have no " + + "id to code mapping [code=" + receiverCode + "]."); + } else { + _receivers.remove(rreg.receiverId); + } _clobj.removeFromReceivers(receiverCode); } } @@ -177,6 +185,7 @@ public class InvocationDirector ListenerMarshaller lm = (ListenerMarshaller)arg; lm.callerOid = _clobj.getOid(); lm.requestId = nextRequestId(); + lm.mapStamp = System.currentTimeMillis(); // create a mapping for this marshaller so that we can // properly dispatch responses sent to it _listeners.put(lm.requestId, lm); @@ -237,7 +246,10 @@ public class InvocationDirector Log.warning("Received invocation response for which we have " + "no registered listener [reqId=" + reqId + ", methId=" + methodId + - ", args=" + StringUtil.toString(args) + "]."); + ", args=" + StringUtil.toString(args) + "]. " + + "It is possble that this listener was flushed " + + "because the response did not arrive within " + + LISTENER_MAX_AGE + " milliseconds."); return; } @@ -254,6 +266,13 @@ public class InvocationDirector ", args=" + StringUtil.toString(args) + "]."); Log.logStackTrace(t); } + + // flush expired listeners periodically + long now = System.currentTimeMillis(); + if (now - _lastFlushTime > LISTENER_FLUSH_INTERVAL) { + _lastFlushTime = now; + flushListeners(now); + } } /** @@ -331,7 +350,33 @@ public class InvocationDirector }); } - /** + /** + * Flushes listener mappings that are older than {@link + * #LISTENER_MAX_AGE} milliseconds. An alternative to flushing + * listeners that did not explicitly receive a response within our + * expiry time period is to have the server's proxy listener send a + * message to the client when it is finalized. We then know that no + * server entity will subsequently use that proxy listener to send a + * response to the client. This involves more network traffic and + * complexity than seems necessary and if a user of the system does + * respond after their listener has been flushed, an informative + * warning will be logged. (Famous last words.) + */ + protected void flushListeners (long now) + { + if (_listeners.size() > 0) { + Iterator iter = _listeners.values().iterator(); + while (iter.hasNext()) { + ListenerMarshaller lm = (ListenerMarshaller)iter.next(); + if (now - lm.mapStamp > LISTENER_MAX_AGE) { + Log.debug("Flushing marshaller " + lm + "."); + iter.remove(); + } + } + } + } + + /** * Used to generate monotonically increasing invocation request ids. */ protected synchronized short nextRequestId () @@ -373,4 +418,13 @@ public class InvocationDirector /** All registered receivers are maintained in a list so that we can * assign receiver ids to them when we go online. */ protected ArrayList _reclist = new ArrayList(); + + /** The last time we flushed our listeners. */ + protected long _lastFlushTime; + + /** The minimum interval between listener flush attempts. */ + protected static final long LISTENER_FLUSH_INTERVAL = 15000L; + + /** Listener mappings older than 90 seconds are reaped. */ + protected static final long LISTENER_MAX_AGE = 90 * 1000L; } diff --git a/src/java/com/threerings/presents/data/InvocationMarshaller.java b/src/java/com/threerings/presents/data/InvocationMarshaller.java index cdfaa89ec..efea71452 100644 --- a/src/java/com/threerings/presents/data/InvocationMarshaller.java +++ b/src/java/com/threerings/presents/data/InvocationMarshaller.java @@ -1,5 +1,5 @@ // -// $Id: InvocationMarshaller.java,v 1.2 2002/09/20 04:52:49 mdb Exp $ +// $Id: InvocationMarshaller.java,v 1.3 2002/12/08 02:18:50 mdb Exp $ package com.threerings.presents.data; @@ -46,6 +46,10 @@ public class InvocationMarshaller * marshalling listener. This is only valid on the client. */ public transient InvocationListener listener; + /** The time at which this listener marshaller was registered. + * This is only valid on the client. */ + public transient long mapStamp; + /** The distributed object manager to use when dispatching proxied * responses. This is only valid on the server. */ public transient DObjectManager omgr;