queue up an invoker unit which would go off and do some database stuff and then
by the time it came back and was ready to publish its results to a distributed
object, the object in question would have been destroyed for any of a variety
of fairly natural reasons (client disconnected or logged off, game was
abandoned, dog ate homework).
One "solution" to this problem would be to litter our games' code with
thousands of calls to isActive() in the handleResult() methods of our invoker
units. We've done a bit of that in Yohoho but I've resisted starting down that
path in our other games.
Another solution would be to create an Invoker.Unit wrapper that takes a
reference to the distributed object (or objects) that it will be modifying and
have the common unit code check that the object(s) in question are still alive
at the end of the asynchronous operation and not call handleResult() if they
are not. This has numerous problems: what do you do if one object is alive but
not another, how do you incorporate this functionality in with the numerous
other Invoker.Unit derivations we have that simplify our lives in other ways
(without getting crazy and starting to use something like AOP), do you silently
abort the operation or log something?
So instead, I've come around to the idea that this is simply a dirty fact of
life in asynchronous programming and the fact that we can accept modifications
to distributed state after the distribted object in question is dead is a good
thing. We used to log a warning every time this happened and freak out even
more substantially if one tried to start a transaction on a dead object. Now we
will simply log an informational message (I don't think this sort of thing
should be silently ignored because there are some cases where it is an
indication of incorrect code, those are simply more rare). We will also allow a
transaction to be started on a dead object and when the transaction is
committed, all the events involved will be dropped just like a single
modification would have been dropped on that object.
This allows the most sensible thing to happen which is any results that are
published to still live objects will actually be published and results
published to dead objects will be dropped without making a big fuss. Since a
dead object by definition cannot have subscribers, no one could possibly have
cared about the dropped events anyway.
Also widened.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4545 542714f4-19e9-0310-aa3c-eee0fc999fb1
looking for something assignable from Object (rather than the other
way around) when the argument was null.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4346 542714f4-19e9-0310-aa3c-eee0fc999fb1
like Groovy and JRuby. Created a DynamicEventDispatcher that automatically maps
attribute, element and set events to methods. Say you have a field:
public DSet occupantInfo;
you can create a method in any class:
public void occupantInfoAdded (BodyObject source, OccupantInfo entry);
and then bind that class as a listener using the dynamic event dispatcher:
_myobj.addListener(new DynamicEventDispatcher(object));
I also created a nicer replacement for the MessageHandler system which is
clunky but still way simpler than using a full InvocationService. Basically we
dispatch MessageEvent as if it were a method call.
For example, in AtlantiManager I define:
public void placeTile (BodyObject placer, AtlantiTile tile)
which receives a request by a player to place a tile on their turn. Then in
AtlantiController, I simply call:
_atlobj.manager.invoke("placeTile", tile);
Of course, in JRuby and Groovy, that's going to look like:
_atlobj.manager.placeTile(tile);
which is all part of the fun.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4344 542714f4-19e9-0310-aa3c-eee0fc999fb1
distributed objects by reflection since we don't allow clients to create
objects, furthermore we needn't do it asynchronously. The object creation
methods were moved into the server-side only interface and made "immediate", so
the caller creates a derived instance of DObject and registers it with the
system instead of creating it with a Subscriber callback.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4342 542714f4-19e9-0310-aa3c-eee0fc999fb1
where we don't think it ever will.
Instead of returning null, or the exception, always throw a runtime
with just the CloneNotSupportedException as the argument, as this will
contain the most information and will preserve the stack trace.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4264 542714f4-19e9-0310-aa3c-eee0fc999fb1
required some serious bending and folding of the generic type system, but for
the most part we managed to avoid any mutilating. The gendobj task now
generates properly typed "addToXXX" and "updateXXX" DSet methods based on the
parameterized type of the DSet. This might cause unrecompiled code to break,
but I don't think there are many cases in the base toolkit where people call
DSet adders or updaters. We'll see and I'll add backwards compatibility
versions for cases where we need them to support GG games (everything else we
can just recompile).
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4245 542714f4-19e9-0310-aa3c-eee0fc999fb1
Pecos. I'll sort out the per-project niggling bits in just a moment.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4209 542714f4-19e9-0310-aa3c-eee0fc999fb1
returns a properly typed clone of the DSet in question.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4164 542714f4-19e9-0310-aa3c-eee0fc999fb1
all generic references to E as DSet.Entry rather than Object which is what I
was assuming. So when I write:
(E[])new Object[...]
that gets rewritten to:
(Entry[])new Object[...]
which causes le freakout. We need to use Entry[] everywhere, which we happened
to be doing for everything except toArray().
I rewrote things to use E[] everywhere now that I know it will get rewritten to
Entry[]. I thought it was going to get rewritten to Object[] before so I didn't
genericize that stuff.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4059 542714f4-19e9-0310-aa3c-eee0fc999fb1
have to be parameterized on the type of the Comparable that is returned by
getKey(), but that turns out to be a twisty maze of generics that is not
obvious to me how to express using Java's generics syntax and it opens up
various cans of worms that I don't have time to solve right now. So we'll
settle for the DSet itself being parameterized on the Entry class and the Entry
class being non-type safe.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4051 542714f4-19e9-0310-aa3c-eee0fc999fb1
magically booches and causes an IncompatibleClassChangeError, so we're just
going to work around it.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4008 542714f4-19e9-0310-aa3c-eee0fc999fb1
(It may be the same thing under the hood, but our pal Occam would approve.)
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3958 542714f4-19e9-0310-aa3c-eee0fc999fb1
than it needs to be.
Broke streaming compatibility in order to be much more efficient over the wire.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3938 542714f4-19e9-0310-aa3c-eee0fc999fb1
optimization in the process.
Previously, we dumped stack on the server if a entry add was illegal but
did nothing for update/remove.
On the client, we complained for updates and (indirectly) adds, but not
removes.
Now we complain everywhere.
And- as an added bonus, on the server we now do only one binary search for
updates and removals. Whee!
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3528 542714f4-19e9-0310-aa3c-eee0fc999fb1
methods they were looking for.
Since it's probably impossible to have proguard generically skip renaming
for these generated method names, let's update the fields in a different way.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3434 542714f4-19e9-0310-aa3c-eee0fc999fb1
'iterator()' name and deprecate the old 'entries()' method.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3405 542714f4-19e9-0310-aa3c-eee0fc999fb1
objects (DSet, arrays) in a DEvent (which should not change as a result of
other events being applied) and those in the object itself (which do
change and evolve as events are applied to the object).
This is important both because the DEvent is passed on to another thread
for delivery to remote clients, thus changes to the values in the event
could take place before they were serialized and sent over the network,
and because compound events are applied to an object before they are sent
to the other thread for delivery and thus, for example, setting a DSet and
then adding a few entries to it in a compound event would result in the
DEvent copy of the DSet becoming corrupted.
Two problems remain (note, neither of these are new, the one issue
introduced when I rewrote the DObject stuff is fixed by these checkins):
1. Object subscription requests are supposed to deliver a snapshot of the
object at the point in the event stream at which the subscription
request was processed, but presently we pass only a reference to the
object off to the networking thread which means that before the object
is serialized and sent to clients, subsequent events could be applied
to it and then those events would be sent to the client as well
resulting in funny business (probably nothing more than duplicate DSet
entry warnings, but imagination and Chapter 17 tell us that worse
things could happen).
2. The use of Streamable instances could result in badness. If a field in
a Streamable is modified and the whole Streamable set() back into the
object to broadcast the update, then further changes were made to the
Streamable before the attribute change event was serialized and sent
over the network, the second modifications would be reflected in the
event triggered by the first modifications.
The first problem may be solvable (albeit inefficiently) by serializing
the DObject on the event dispatcher thread and sending that serialized
copy off to the network thread for delivery to the client. It would be
much less efficient as we would be unable to make use of the client's
already "primed" ObjectOutputStream which may have already mapped many of
the classes in the object to two byte codes, but object subscription is
fairly uncommon compared to delivery of events, so inefficiency might not
be a big problem in this case.
The second problem might be solved by requiring that all Streamable
implementations implement clone() and then cloning any Streamable
attribute just as we do an array or DSet during an attribute, array
element or DSet entry change. This would be a more significant performance
hit as well as require a review of all of our Streamable classes (to
determine if they need a custom clone() implementation), and it has up to
now not actually manifested as a problem.
In any case I'm not going to tackle either of these remedies at the moment
because I'm on vacation, dammit.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3294 542714f4-19e9-0310-aa3c-eee0fc999fb1
accomplish our "previous value" support in the distributed object system
without using reflection and could also avoid using reflection in the case
where we have already applied the event on the server (which is generally
the case on the server).
Rather than hacking up the gendobj script, I took this opportunity also to
rewrite the DObject generation script as an Ant task and in doing so,
implemented another recent idea which is that we can just augment the
FooObject.java file instead of having a separate .dobj and .java file.
You'd think it was spring there's so much cleaning going on.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3284 542714f4-19e9-0310-aa3c-eee0fc999fb1
before it has a chance to bring everything to a screeching halt.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3051 542714f4-19e9-0310-aa3c-eee0fc999fb1
let's just not do it. We can accomplish our earlier goals, though with
slightly less distinction making capability, in a different way.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3050 542714f4-19e9-0310-aa3c-eee0fc999fb1
was once alive but is now gone. Catch attempts to start a transaction on a
destroyed object and log them as such. Made isDestroyed() and isActive()
final for wholly unfounded performance reasons.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3045 542714f4-19e9-0310-aa3c-eee0fc999fb1
post events to an object that is already destroyed, we'll allow
transactions to be started on non-active objects and we'll just log a
warning when the transaction is finally committed like we do for all other
events on non-active objects.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2835 542714f4-19e9-0310-aa3c-eee0fc999fb1