From 0bbb6759db1521895c3c286a5a0b5611e85e7e3b Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 11 Dec 2003 18:36:55 +0000 Subject: [PATCH] Yes kids, it's important to be safe when you're playing in the dangerous playground of distributed object subscription. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2904 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../presents/util/SafeSubscriber.java | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 src/java/com/threerings/presents/util/SafeSubscriber.java diff --git a/src/java/com/threerings/presents/util/SafeSubscriber.java b/src/java/com/threerings/presents/util/SafeSubscriber.java new file mode 100644 index 000000000..58ee019dd --- /dev/null +++ b/src/java/com/threerings/presents/util/SafeSubscriber.java @@ -0,0 +1,190 @@ +// +// $Id: SafeSubscriber.java,v 1.1 2003/12/11 18:36:55 mdb Exp $ + +package com.threerings.presents.util; + +import com.samskivert.util.StringUtil; + +import com.threerings.presents.Log; +import com.threerings.presents.dobj.DObject; +import com.threerings.presents.dobj.DObjectManager; +import com.threerings.presents.dobj.ObjectAccessException; +import com.threerings.presents.dobj.Subscriber; + +/** + * A class that safely handles the asynchronous subscription to a + * distributed object when it is not know if the subscription will + * complete before the subscriber decides they no longer wish to be + * subscribed. + */ +public class SafeSubscriber + implements Subscriber +{ + /** + * Creates a safe subscriber for the specified distributed object + * which will interact with the specified subscriber. + */ + public SafeSubscriber (int oid, Subscriber subscriber) + { + // make sure they're not fucking us around + if (oid <= 0) { + throw new IllegalArgumentException( + "Invalid oid provided to safesub [oid=" + oid + "]"); + } + if (subscriber == null) { + throw new IllegalArgumentException( + "Null subscriber provided to safesub [oid=" + oid + "]"); + } + + _oid = oid; + _subscriber = subscriber; + } + + /** + * Initiates the subscription process. + */ + public void subscribe (DObjectManager omgr) + { + if (_active) { + Log.warning("Active safesub asked to resubscribe " + this + "."); + Thread.dumpStack(); + return; + } + + // note that we are now again in the "wishing to be subscribed" state + _active = true; + + // make sure we dont have an object reference (which should be + // logically impossible) + if (_object != null) { + Log.warning("Incroyable! A safesub has an object and was " + + "non-active!? " + this + "."); + Thread.dumpStack(); + // make do in the face of insanity + _subscriber.objectAvailable(_object); + return; + } + + if (_pending) { + // we were previously asked to subscribe, then they asked to + // unsubscribe and now they've asked to subscribe again, all + // before the original subscription even completed; we need do + // nothing here except as the original subscription request + // will eventually come through and all will be well + return; + } + + // we're not pending and we just became active, that means we need + // to request to subscribe to our object + _pending = true; + omgr.subscribeToObject(_oid, this); + } + + /** + * Terminates the object subscription. If the initial subscription has + * not yet completed, the desire to terminate will be noted and the + * subscription will be terminated as soon as it completes. + */ + public void unsubscribe (DObjectManager omgr) + { + if (!_active) { + Log.warning("Unactive safesub asked to unsubscribe " + this + "."); + Thread.dumpStack(); + // but go ahead anyway in case we are subscribed + } + + // note that we no longer desire to be subscribed + _active = false; + + if (_pending) { + // make sure we don't have an object reference + if (_object != null) { + Log.warning("Incroyable! A safesub has an object and is " + + "pending!? " + this + "."); + Thread.dumpStack(); + } else { + // nothing to do but wait for the subscription to complete + // at which point we'll pitch the object post-haste + return; + } + } + + // make sure we have our object + if (_object == null) { + Log.warning("Zut alors! A safesub _was_ active and not " + + "pending yet has no object!? " + this + "."); + Thread.dumpStack(); + // nothing to do since we're apparently already unsubscribed + return; + } + + // finally effect our unsubscription + _object = null; + omgr.unsubscribeFromObject(_oid, this); + } + + // documentation inherited from interface + public void objectAvailable (DObject object) + { + // make sure life is not too cruel + if (_object != null) { + Log.warning("Madre de dios! Our object came available but " + + "we've already got one!? " + this); + // go ahead and pitch the old one, God knows what's going on + _object = null; + } + if (!_pending) { + Log.warning("J.C. on a pogo stick! Our object came available " + + "but we're not pending!? " + this); + // go with our badselves, it's the only way + } + + // we're no longer pending + _pending = false; + + // if we are no longer active, we don't want this damned thing + if (!_active) { + DObjectManager omgr = object.getManager(); + // if the object's manager is null, that means the object is + // already destroyed and we need not trouble ourselves with + // unsubscription as it has already been pitched to the dogs + if (omgr != null) { + omgr.unsubscribeFromObject(_oid, this); + } + return; + } + + // otherwise the air is fresh and clean and we can do our job + _object = object; + _subscriber.objectAvailable(object); + } + + // documentation inherited from interface + public void requestFailed (int oid, ObjectAccessException cause) + { + // if we're active, let our subscriber know that the shit hit the fan + if (_active) { + _subscriber.requestFailed(oid, cause); + // and deactivate ourselves as we never got our object (and + // thus the real subscriber need not call unsubscribe()) + _active = false; + } + } + + /** + * Returns a string representation of this instance. + */ + public String toString () + { + return "[oid=" + _oid + + ", sub=" + StringUtil.shortClassName(_subscriber) + + ", active=" + _active + ", pending=" + _pending + + ", dobj=" + StringUtil.shortClassName(_object) + "]"; + } + + protected int _oid; + protected Subscriber _subscriber; + protected DObject _object; + protected boolean _active; + protected boolean _pending; +}