ThingchangedEvents are used internally by VisAD to efficiently
trasmit changes from a Thing to an Action.
Unlike most other events in VisAD, ThingChangedEvents are "lossy"
in that not all of them are delivered. This is by design, because
once a Thing is changed, it stays changed relative to an Action
until that Action acknowledges the change. The somewhat complex
interplay between a Thing and an Action is designed to minimize
the amount of work done by an Action as the result of a change.
Sending a ThingChangedEvent from ThingReferenceImpl to ActionImpl
A ThingReferenceImpl is associated with an ActionImpl through the
addThingChangedListener method. Rather than storing
the ActionImpl directly, a reference to it is kept
in a ThingChangedLink object. Since multiple ActionImpls can
be associated with a single ThingReferenceImpl, each ThingReferenceImpl
keeps a list of ThingChangedLinks.
A ThingChangedEvent is created in the incTick() method
(which is called whenever the associated Thing is changed) and
passed on to all the Actions via the ThingChangedLinks'
queueThingChangedEvent method.
ThingReferenceImpl can also return a ThingChangedEvent via
its acknowledgeThingChanged method, but this
should only be called by ReferenceActionLink's
getThingChangedEvent method. This is described
more fully below.
(It would be nice if the acknowledgeThingChanged code
could be moved directly
into ThingChangedLink's getThingChangedEvent,
but this proves to be difficult. Since either the ThingReferenceImpl
or the Action could be on a
a remote machine, the method must reside in the ThingReferenceImpl
which has a Remote implementation, rather than in ThingChangedLink
which does not.)
Each ThingChangedLink contains a reference to the
associated Action, along with space for a single saved
ThingChangedEvent. Since ThingChangedEvents only indicate that
the associated Thing has changed, only the most recent event needs to be
saved.
As described above, a ThingChangedLink receives a ThingChangedEvent
from its ThingReferenceImpl via the ThingChangedLink's
queueThingChangedEvent method. If the Action isn't
currently processing an event from this
ThingChangedLink, queueThingChangedEvent
passes the event on to the Action via the Action's thingChanged
method and the ThingChangedLink notes that the Action is busy
(by setting its internal Ball variable to
false.) If the Action is busy
(because Ball is false)
queueThingChangedEvent saves the event.
An Action dequeues ThingChangedEvents by calling
the getThingChangedEvent method for all of its
ReferenceActionLinks. The ReferenceActionLink's
getThingChangedEvent calls the associated ThingReferenceImpl's
acknowledgeThingChanged method, which finds the
appropriate ThingChangedLink and calls its
acknowledgeThingChangedEvent
method. If the ThingChangedLink doesn't have an event saved,
it'll set its internal Ball variable to false
(to indicate that the Action is no longer busy) and return null.
If there's an event to return, acknowledgeThingChangedEvent
returns it, leaving Ball set to false to
indicate that the Action is still busy.
Doing things this way ensures that Action is called as
soon as an associated Thing changes, but once it's been
notified the Action isn't actively called again until it
has finished handling all queued changes. This means that
a Thing can be changed as frequently as necessary, but the
Action only sees as many changes as necessary to model the
change.
For example, some meter might be moved from a value of
1 to a value of 100 in a couple of seconds, but it might
take the Action a half-second or so to deal with each change.
Rather than
forcing Action to deal with all the values sequentially
(a process which would take 50 seconds or so), the code
instead lets Action handle the first value, then skip over
a bunch of intermediate values to the next current value
until it reaches the final value. Rather than dealing with
100 values, Action would only need to deal with 4 or 5.
Just as ThingReferenceImpl tracks its associated Actions through
ThingChangedLinks, so ActionImpl tracks its associated ThingReferences
via a list of ReferenceActionLinks. In fact, there should be
a corresponding ReferenceActionLink for every ThingChangedLink.
However, ReferenceActionLink's
logic is much simpler than ThingChangedLink.
As stated above,
ReferenceActionLink's getThingChangedEvent basically
acts as a front-end for the corresponding ThingChangedLink's
acknowledgeThingChangedEvent, through the ThingReference's
acknowledgeThingChanged, which
finds the ThingChangedLink for the supplied Action and calls
that ThingChangedLink's acknowledgeThingChangedLink
method.
ReferenceActionLink also has a Ball variable, but this one keeps
things in lock-step, since a getThingChangedEvent
which returns a ThingChangedEvent (and thus sets ReferenceActionLink's
Ball to false) must be followed by a
call to the ReferenceActionLink's
acknowledgeThingChangedEvent method
(which sets Ball
back to true) before getThingChangedEvent
will return any more events.
ActionImpl is the final target of all ThingChangedEvents.
These are dealt with by an ActionImpl worker thread in
the run method, by handing them off to the
ActionImpl's thingChanged
method.
The thingChanged method sends
an acknowledgement back to the
ReferenceActionLink (and releasing the
implicit lock set by the
call to the ReferenceActionLink's getThingChangedEvent
It also queues up worker thread, which will eventually call the
ActionImpl's doAction method, which is what this
is all about.
Last modified: Tue Feb 12 16:18:05 CST 2002