diff --git a/src/java/com/threerings/presents/data/ClientObject.dobj b/src/java/com/threerings/presents/data/ClientObject.dobj
index a022ba8c2..c4f0e4426 100644
--- a/src/java/com/threerings/presents/data/ClientObject.dobj
+++ b/src/java/com/threerings/presents/data/ClientObject.dobj
@@ -1,11 +1,13 @@
//
-// $Id: ClientObject.dobj,v 1.2 2002/09/19 23:36:59 mdb Exp $
+// $Id: ClientObject.dobj,v 1.3 2002/11/26 02:14:24 mdb Exp $
package com.threerings.presents.data;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DSet;
+import com.threerings.presents.Log;
+
/**
* Every client in the system has an associated client object to which
* only they subscribe. The client object can be used to deliver messages
@@ -29,4 +31,32 @@ public class ClientObject extends DObject
{
return "(" + getOid() + ")";
}
+
+ /**
+ * Used for reference counting client objects, adds a reference to
+ * this object.
+ */
+ public synchronized void reference ()
+ {
+ _references++;
+// Log.info("Incremented references [who=" + who() +
+// ", refs=" + _references + "].");
+ }
+
+ /**
+ * Used for reference counting client objects, releases a reference to
+ * this object.
+ *
+ * @return true if the object has remaining references, false
+ * otherwise.
+ */
+ public synchronized boolean release ()
+ {
+// Log.info("Decremented references [who=" + who() +
+// ", refs=" + (_references-1) + "].");
+ return (--_references > 0);
+ }
+
+ /** Used to reference count resolved client objects. */
+ protected transient int _references;
}
diff --git a/src/java/com/threerings/presents/data/ClientObject.java b/src/java/com/threerings/presents/data/ClientObject.java
index 69f1ea81d..5ac7d9fb4 100644
--- a/src/java/com/threerings/presents/data/ClientObject.java
+++ b/src/java/com/threerings/presents/data/ClientObject.java
@@ -1,11 +1,13 @@
//
-// $Id: ClientObject.java,v 1.4 2002/09/19 23:36:59 mdb Exp $
+// $Id: ClientObject.java,v 1.5 2002/11/26 02:14:24 mdb Exp $
package com.threerings.presents.data;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DSet;
+import com.threerings.presents.Log;
+
/**
* Every client in the system has an associated client object to which
* only they subscribe. The client object can be used to deliver messages
@@ -33,6 +35,34 @@ public class ClientObject extends DObject
return "(" + getOid() + ")";
}
+ /**
+ * Used for reference counting client objects, adds a reference to
+ * this object.
+ */
+ public synchronized void reference ()
+ {
+ _references++;
+// Log.info("Incremented references [who=" + who() +
+// ", refs=" + _references + "].");
+ }
+
+ /**
+ * Used for reference counting client objects, releases a reference to
+ * this object.
+ *
+ * @return true if the object has remaining references, false
+ * otherwise.
+ */
+ public synchronized boolean release ()
+ {
+// Log.info("Decremented references [who=" + who() +
+// ", refs=" + (_references-1) + "].");
+ return (--_references > 0);
+ }
+
+ /** Used to reference count resolved client objects. */
+ protected transient int _references;
+
/**
* Requests that the specified entry be added to the
* receivers set. The set will not change until the event is
diff --git a/src/java/com/threerings/presents/server/ClientManager.java b/src/java/com/threerings/presents/server/ClientManager.java
index 06afd3abb..18f5a0517 100644
--- a/src/java/com/threerings/presents/server/ClientManager.java
+++ b/src/java/com/threerings/presents/server/ClientManager.java
@@ -1,5 +1,5 @@
//
-// $Id: ClientManager.java,v 1.26 2002/11/05 02:17:56 mdb Exp $
+// $Id: ClientManager.java,v 1.27 2002/11/26 02:14:25 mdb Exp $
package com.threerings.presents.server;
@@ -135,7 +135,9 @@ public class ClientManager
* If the client object is already resolved, the request will be
* processed immediately, otherwise the appropriate client object will
* be instantiated and populated by the registered client resolver
- * (which may involve talking to databases).
+ * (which may involve talking to databases). Note: this
+ * must be paired with a call to {@link #releaseClientObject}
+ * when the caller is finished with the client object.
*/
public synchronized void resolveClientObject (
String username, ClientResolutionListener listener)
@@ -144,6 +146,7 @@ public class ClientManager
String key = toKey(username);
ClientObject clobj = (ClientObject)_objmap.get(key);
if (clobj != null) {
+ clobj.reference();
listener.clientResolved(username, clobj);
return;
}
@@ -189,67 +192,34 @@ public class ClientManager
}
/**
- * If an entity resolves a client object outside the scope of a normal
- * client session, it should call this to unmap the client object when
- * it's finished. This won't actually unmap the client if someone came
- * along and started a session for that client in the meanwhile, but
- * if it does unmap the client it will also destroy the client object
- * (finishing the job, as it were).
+ * Releases a client object that was obtained via a call to {@link
+ * #resolveClientObject}. If this caller is the last reference, the
+ * object will be flushed and destroyed.
*/
- public synchronized void unmapClientObject (String username)
+ public void releaseClientObject (String username)
{
- // we only remove the mapping if there's not a session in progress
- // (which is indicated by an entry in the locks table)
String key = toKey(username);
- if (_locks.contains(key)) {
+ ClientObject clobj = (ClientObject)_objmap.get(key);
+ if (clobj == null) {
+ Log.warning("Requested to release unmapped client object " +
+ "[username=" + username + "].");
+ Thread.dumpStack();
return;
}
- ClientObject clobj = (ClientObject)_objmap.remove(key);
- if (clobj != null) {
- PresentsServer.omgr.destroyObject(clobj.getOid());
- } else {
- Log.warning("Requested to unmap non-existent client object " +
- "[username=" + username + "].");
+ // decrement the reference count and stop here if there are
+ // remaining references
+ if (clobj.release()) {
+ return;
}
- }
- /**
- * When a client object becomes part of an active session, this method
- * should be called to ensure that it is not unloaded by any entities
- * that temporarily resolve and release the object. This is called
- * automatically when a real user starts a session by establishing a
- * network connection with the server. If a client session is managed
- * via some other mechanism (bots managed by the server, for example),
- * this method and its corresponding {@link #releaseClientObject}
- * should be called at the beginning and end of the faked client
- * session respectively.
- */
- public synchronized void lockClientObject (String username)
- {
- String key = toKey(username);
- if (_locks.contains(key)) {
- Log.warning("Requested to lock already locked user " +
- "[username=" + username + "].");
- Thread.dumpStack();
+ Log.debug("Destroying client " + clobj.who() + ".");
- } else {
- _locks.add(key);
- }
- }
+ // we're all clear to go; remove the mapping
+ _objmap.remove(key);
- /**
- * Releases a client object when their session has ended.
- *
- * @see #lockClientObject
- */
- public synchronized void releaseClientObject (String username)
- {
- if (!_locks.remove(toKey(username))) {
- Log.warning("Requested to unlock a user that was not locked " +
- "[username=" + username + "].");
- Thread.dumpStack();
- }
+ // and destroy the object itself
+ PresentsServer.omgr.destroyObject(clobj.getOid());
}
// documentation inherited
@@ -355,21 +325,12 @@ public class ClientManager
if (rc == null) {
Log.warning("Unregistered client ended session " + client + ".");
Thread.dumpStack();
-
- // if they weren't in the username mapping, bail out now
- // because the subsequent unmappings would just fail if we
- // tried to do them for an unmapped client
- return;
-
} else if (rc != client) {
Log.warning("Different clients with same username!? " +
"[c1=" + rc + ", c2=" + client + "].");
} else {
Log.info("Ending session " + client + ".");
}
-
- // unmap (and destroy) the client object
- unmapClientObject(username);
}
/**
diff --git a/src/java/com/threerings/presents/server/ClientResolver.java b/src/java/com/threerings/presents/server/ClientResolver.java
index f6a1f922b..c6d3e1bb2 100644
--- a/src/java/com/threerings/presents/server/ClientResolver.java
+++ b/src/java/com/threerings/presents/server/ClientResolver.java
@@ -1,5 +1,5 @@
//
-// $Id: ClientResolver.java,v 1.1 2002/03/05 05:33:25 mdb Exp $
+// $Id: ClientResolver.java,v 1.2 2002/11/26 02:14:25 mdb Exp $
package com.threerings.presents.server;
@@ -99,6 +99,8 @@ public class ClientResolver extends Invoker.Unit
ClientResolutionListener crl = (ClientResolutionListener)
_listeners.get(i);
try {
+ // add a reference for each listener
+ _clobj.reference();
crl.clientResolved(_username, _clobj);
} catch (Exception e) {
Log.warning("Client resolution listener choked during " +
diff --git a/src/java/com/threerings/presents/server/PresentsClient.java b/src/java/com/threerings/presents/server/PresentsClient.java
index 1c2ca0b3a..28dc17edf 100644
--- a/src/java/com/threerings/presents/server/PresentsClient.java
+++ b/src/java/com/threerings/presents/server/PresentsClient.java
@@ -1,5 +1,5 @@
//
-// $Id: PresentsClient.java,v 1.44 2002/11/05 05:47:36 mdb Exp $
+// $Id: PresentsClient.java,v 1.45 2002/11/26 02:14:25 mdb Exp $
package com.threerings.presents.server;
@@ -154,20 +154,13 @@ public class PresentsClient
// call down to any derived classes
clientObjectWillChange(_clobj, clobj);
- // release our old client object
+ // release our old client object; this will destroy it
_cmgr.releaseClientObject(_username);
- // unmap the old client object; this will destroy it and
- // clear out our username mapping
- _cmgr.unmapClientObject(_username);
-
// update our internal fields
_username = username;
_clobj = clobj;
- // lock our new client object
- _cmgr.lockClientObject(_username);
-
// call down to any derived classes
clientObjectDidChange(_clobj);
@@ -234,10 +227,6 @@ public class PresentsClient
// obtain our starting username
assignStartingUsername();
- // obtain a lock for our username->client object mapping while we
- // have an active session
- cmgr.lockClientObject(_username);
-
// resolve our client object before we get fully underway
cmgr.resolveClientObject(_username, this);
@@ -448,11 +437,10 @@ public class PresentsClient
// about to generate
clearSubscrips();
- // release our locked client object
+ // release (and destroy) our client object
_cmgr.releaseClientObject(_username);
- // then let the client manager know what's up (it will take care
- // of destroying our client object for us)
+ // let the client manager know that we're audi 5000
_cmgr.clientDidEndSession(this);
// clear out the client object so that we know the session is over