Widened, added a workaround for a "doesn't actually break anything and is too

complex to properly fix right now" bug.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4713 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2007-05-18 02:46:47 +00:00
parent c2fb8f99c7
commit 0c90344e6b
4 changed files with 62 additions and 50 deletions
@@ -71,8 +71,8 @@ public abstract class DEvent implements Streamable
* @exception ObjectAccessException thrown if there is any problem applying the event to the * @exception ObjectAccessException thrown if there is any problem applying the event to the
* object (invalid attribute, etc.). * object (invalid attribute, etc.).
* *
* @return true if the object manager should go on to notify the object's subscribers of this * @return true if the object manager should go on to notify the object's listeners of this
* event, false if the event should be treated silently and the subscribers should not be * event, false if the event should be treated silently and the listeners should not be
* notified. * notified.
*/ */
public abstract boolean applyToObject (DObject target) public abstract boolean applyToObject (DObject target)
@@ -26,22 +26,20 @@ import com.samskivert.util.StringUtil;
import com.threerings.presents.Log; import com.threerings.presents.Log;
/** /**
* An entry added event is dispatched when an entry is added to a {@link * An entry added event is dispatched when an entry is added to a {@link DSet} attribute of a
* DSet} attribute of a distributed entry. It can also be constructed to * distributed entry. It can also be constructed to request the addition of an entry to a set and
* request the addition of an entry to a set and posted to the dobjmgr. * posted to the dobjmgr.
* *
* @see DObjectManager#postEvent * @see DObjectManager#postEvent
*/ */
public class EntryAddedEvent<T extends DSet.Entry> extends NamedEvent public class EntryAddedEvent<T extends DSet.Entry> extends NamedEvent
{ {
/** /**
* Constructs a new entry added event on the specified target object * Constructs a new entry added event on the specified target object with the supplied set
* with the supplied set attribute name and entry to add. * attribute name and entry to add.
* *
* @param targetOid the object id of the object to whose set we will * @param targetOid the object id of the object to whose set we will add an entry.
* add an entry. * @param name the name of the attribute to which to add the specified entry.
* @param name the name of the attribute to which to add the specified
* entry.
* @param entry the entry to add to the set attribute. * @param entry the entry to add to the set attribute.
*/ */
public EntryAddedEvent (int targetOid, String name, T entry) public EntryAddedEvent (int targetOid, String name, T entry)
@@ -50,11 +48,9 @@ public class EntryAddedEvent<T extends DSet.Entry> extends NamedEvent
} }
/** /**
* Used when the distributed object already added the entry before * Used when the distributed object already added the entry before generating the event.
* generating the event.
*/ */
public EntryAddedEvent (int targetOid, String name, T entry, public EntryAddedEvent (int targetOid, String name, T entry, boolean alreadyApplied)
boolean alreadyApplied)
{ {
super(targetOid, name); super(targetOid, name);
_entry = entry; _entry = entry;
@@ -62,8 +58,8 @@ public class EntryAddedEvent<T extends DSet.Entry> extends NamedEvent
} }
/** /**
* Constructs a blank instance of this event in preparation for * Constructs a blank instance of this event in preparation for unserialization from the
* unserialization from the network. * network.
*/ */
public EntryAddedEvent () public EntryAddedEvent ()
{ {
@@ -86,7 +82,31 @@ public class EntryAddedEvent<T extends DSet.Entry> extends NamedEvent
if (!_alreadyApplied) { if (!_alreadyApplied) {
boolean added = target.getSet(_name).add(_entry); boolean added = target.getSet(_name).add(_entry);
if (!added) { if (!added) {
Log.warning("Duplicate entry found [event=" + this + "]."); // If this entry is already in the set, then don't notify our listeners of the
// event add, because nothing was actually added; the DSet will have already
// complained; this happens occasionally due to a race condition between client
// object subscription and set addition, for example, the follow sequence of events
// can take place:
//
// 1. SUBSCRIBE arrives from client, AccessObjectEvent posted;
//
// 2. addToSet() called on server, value immediately added to set and
// EntryAddedEvent is posted;
//
// 3. AccessObjectEvent processed; client is added to proxy subscriber list,
// DObject serialized and sent to client; client receives object which already has
// the entry in the set;
//
// 4. EntryAddedEvent processed, client is a subscriber so it is forwarded along;
// when it arrives at the client, we find ourselves in this situation.
//
// Fixing this would require either a) giving up immediate adding to the DSet when
// an event is posted, but we add/update immediately because it makes our lives
// *vastly* simpler in a thousand other cases, or b) doing some magic in the DSet
// where the server "sees" the new entry added, but if the DSet is serialized
// before the EntryAddedEvent comes through to "commit" that entry it is not
// included. This is probably a good idea and maybe we'll do it sometime.
return false;
} }
} }
return true; return true;
@@ -111,8 +131,7 @@ public class EntryAddedEvent<T extends DSet.Entry> extends NamedEvent
protected T _entry; protected T _entry;
/** Used when this event is generated on the authoritative server /** Used when this event is generated on the authoritative server where object changes are made
* where object changes are made immediately. This lets us know not to * immediately. This lets us know not to apply ourselves when we're actually dispatched. */
* apply ourselves when we're actually dispatched. */
protected transient boolean _alreadyApplied; protected transient boolean _alreadyApplied;
} }
@@ -24,28 +24,24 @@ package com.threerings.presents.dobj;
import com.threerings.presents.Log; import com.threerings.presents.Log;
/** /**
* An entry removed event is dispatched when an entry is removed from a * An entry removed event is dispatched when an entry is removed from a {@link DSet} attribute of a
* {@link DSet} attribute of a distributed object. It can also be * distributed object. It can also be constructed to request the removal of an entry from a set and
* constructed to request the removal of an entry from a set and posted to * posted to the dobjmgr.
* the dobjmgr.
* *
* @see DObjectManager#postEvent * @see DObjectManager#postEvent
*/ */
public class EntryRemovedEvent<T extends DSet.Entry> extends NamedEvent public class EntryRemovedEvent<T extends DSet.Entry> extends NamedEvent
{ {
/** /**
* Constructs a new entry removed event on the specified target object * Constructs a new entry removed event on the specified target object with the supplied set
* with the supplied set attribute name and entry key to remove. * attribute name and entry key to remove.
* *
* @param targetOid the object id of the object from whose set we will * @param targetOid the object id of the object from whose set we will remove an entry.
* remove an entry. * @param name the name of the attribute from which to remove the specified entry.
* @param name the name of the attribute from which to remove the
* specified entry.
* @param key the entry key that identifies the entry to remove. * @param key the entry key that identifies the entry to remove.
* @param oldEntry the previous value of the entry. * @param oldEntry the previous value of the entry.
*/ */
public EntryRemovedEvent (int targetOid, String name, Comparable key, public EntryRemovedEvent (int targetOid, String name, Comparable key, T oldEntry)
T oldEntry)
{ {
super(targetOid, name); super(targetOid, name);
_key = key; _key = key;
@@ -53,8 +49,8 @@ public class EntryRemovedEvent<T extends DSet.Entry> extends NamedEvent
} }
/** /**
* Constructs a blank instance of this event in preparation for * Constructs a blank instance of this event in preparation for unserialization from the
* unserialization from the network. * network.
*/ */
public EntryRemovedEvent () public EntryRemovedEvent ()
{ {
@@ -88,8 +84,7 @@ public class EntryRemovedEvent<T extends DSet.Entry> extends NamedEvent
_oldEntry = set.removeKey(_key); _oldEntry = set.removeKey(_key);
if (_oldEntry == null) { if (_oldEntry == null) {
// complain if there was actually nothing there // complain if there was actually nothing there
Log.warning("No matching entry to remove [key=" + _key + Log.warning("No matching entry to remove [key=" + _key + ", set=" + set + "].");
", set=" + set + "].");
return false; return false;
} }
} }
@@ -113,6 +108,7 @@ public class EntryRemovedEvent<T extends DSet.Entry> extends NamedEvent
} }
protected Comparable _key; protected Comparable _key;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected transient T _oldEntry = (T)UNSET_OLD_ENTRY; protected transient T _oldEntry = (T)UNSET_OLD_ENTRY;
} }
@@ -26,22 +26,19 @@ import com.samskivert.util.StringUtil;
import com.threerings.presents.Log; import com.threerings.presents.Log;
/** /**
* An entry updated event is dispatched when an entry of a {@link DSet} is * An entry updated event is dispatched when an entry of a {@link DSet} is updated. It can also be
* updated. It can also be constructed to request the update of an entry * constructed to request the update of an entry and posted to the dobjmgr.
* and posted to the dobjmgr.
* *
* @see DObjectManager#postEvent * @see DObjectManager#postEvent
*/ */
public class EntryUpdatedEvent<T extends DSet.Entry> extends NamedEvent public class EntryUpdatedEvent<T extends DSet.Entry> extends NamedEvent
{ {
/** /**
* Constructs a new entry updated event on the specified target object * Constructs a new entry updated event on the specified target object for the specified set
* for the specified set name and with the supplied updated entry. * name and with the supplied updated entry.
* *
* @param targetOid the object id of the object to whose set we will * @param targetOid the object id of the object to whose set we will add an entry.
* add an entry. * @param name the name of the attribute in which to update the specified entry.
* @param name the name of the attribute in which to update the
* specified entry.
* @param entry the entry to update. * @param entry the entry to update.
* @param oldEntry the previous value of the entry. * @param oldEntry the previous value of the entry.
*/ */
@@ -53,8 +50,8 @@ public class EntryUpdatedEvent<T extends DSet.Entry> extends NamedEvent
} }
/** /**
* Constructs a blank instance of this event in preparation for * Constructs a blank instance of this event in preparation for unserialization from the
* unserialization from the network. * network.
*/ */
public EntryUpdatedEvent () public EntryUpdatedEvent ()
{ {
@@ -89,8 +86,7 @@ public class EntryUpdatedEvent<T extends DSet.Entry> extends NamedEvent
_oldEntry = set.update(_entry); _oldEntry = set.update(_entry);
if (_oldEntry == null) { if (_oldEntry == null) {
// complain if we didn't update anything // complain if we didn't update anything
Log.warning("No matching entry to update [entry=" + this + Log.warning("No matching entry to update [entry=" + this + ", set=" + set + "].");
", set=" + set + "].");
return false; return false;
} }
} }
@@ -115,6 +111,7 @@ public class EntryUpdatedEvent<T extends DSet.Entry> extends NamedEvent
} }
protected T _entry; protected T _entry;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected transient T _oldEntry = (T)UNSET_OLD_ENTRY; protected transient T _oldEntry = (T)UNSET_OLD_ENTRY;
} }