Toolbox filter delivery
Overview
Toolbox filters are used by Object and Window Gadget modules to interact with the events delivered to an application by the Wimp. These events may be Toolbox events (generated by Toolbox itself), Wimp events, or Wimp messages. The events are dispatched in order of registration as of Toolbox 1.65.
Each registrant is offered the event if its requested criteria is matched. The registrant is expected to return a state for the event - either to pass it on to the application unaffected, to pass on but with an updated Toolbox ID block, or to swallow the events. For reference, these are represented by the values 0, 1 and -1 respectively.
If any of the registrants updates the Toolbox ID block the entire list of registrants is processed again to give claimants interested in specific toolbox events an opportunity to respond. This is repeated until the ID block remains constant. At this point the Toolbox module returns from the filter, either passing the event on or claiming it as requested by the registrants - 'swallow' states are dominant in deciding the action to take.
Sequenced example
This method has a flaw which can be observed by clients watching for events which cause the ID Block to be updated earlier in the list. This is best described by example.
The Window module has registered a wimp filter for Key Press events such that it may update the ID Block with the object and component handles.
Our new object, which we will call Tracker, has registered a wimp filter for Key Press events
A key is pressed by the user, resulting in a Key Pressed event arriving at the Toolbox filters. Toolbox initially starts with a null ID Block.
- The Window's filter is called, telling it that a Wimp Key Pressed event has occurred. The ID Block contains null handles.
- The Window updates the ID Block to contain its object and component handles.
- The Window returns the 'updated' state (1).
The toolbox sees that the Window module has indeed updated the block and remembers that it must reprocess the list.
- Tracker's filter is called, telling that a Wimp Key Pressed event has occurred. The ID Block contains the Window object and component handles.
- Tracker processes the event as it sees fit, but updates nothing.
- Tracker returns the 'pass on' state (0).
The Toolbox finishes processing its list of filters and sees that it must reprocess the list and so does so.
- The Window's filter is called, telling it that a Wimp Key Pressed event has occurred. The ID Block contains the Window object and component handles.
- The Window sees that the object is not null and so does nothing.
- The Window returns the 'pass on' state (0).
- Tracker's filter is called, telling that a Wimp Key Pressed event has occurred. The ID Block contains the Window object and component handles.
- Tracker processes the event as it sees fit, but updates nothing.
- Tracker returns the 'pass on' state (0).
The Toolbox finishes processing its list of filters and sees that nothing has changed. It passes on the event to the application because it has not been claimed.
The Problem
The problem, from the Tracker's point of view, is that it appears to it that the user pressed a key twice. From its viewpoint there is nothing to distinguish the two events. The reason the problem occurs is that later claimants receive the same ID Block which was returned from the previous claimant, which they will again see when the filters are called for the second iteration.
The solution
The solution is to maintain the current ID Block for the current iteration. Any updates to the ID Block are remembered but only take effect at the next iteration. This prevents filters from seeing the event with the same ID Block twice.
There is an argument that this prevents potential shortcuts where multiple iterations are reduced to just two iterations (or even one, if the final registrant claims the event). However, this seeming gain is through fortuity for a particular case rather than a consistent design. By ensuring that a consistent set of events are seen by all registrants we can prevent any form of race conditions based on module or registration order.
There is a potential for conflict with this implementation if two registrants update the ID Block during an iteration - which one should be taken as the correct form to pass to the next iteration (or to the user) ? The situations where this occur would have to be contrived at best. Such an example might occur if a registrant was using a Window object to provide its implementation, but was monitoring the events for the underlying Wimp handle, just as the Window module itself does. In this particular case, the ordering of dispatch will mean that the Window module should be called first, and the other module second. As such, it would make sense that later registrants updating the ID Block be dominant. However, this should be considered a stop-gap measure for clients which are monitoring inappropriate fields. Where Toolbox objects are used, it is more appropriate (and partially necessary for this solution, if not for the current state of the Toolbox) to monitor Toolbox object identifiers.
Side effects
At the present time the current implementation presents no problems to filter registrants because of either defensive design due to this very problem, or fortuity. The effect of the new algorithm will be that registrants must be careful to check for only the state they are interested in. In particular, if they are looking for Wimp handles within the event, they should only perform the checks if the ID Block contains null handles. Failure to do this may result in seemingly duplicated events being processed by the registrant, where previously only a single event might have been seen.
Processing time for the filters will be variable due to this change. On the one hand, because of the fewer 'duplicate' events with a filled in ID Block, the processing load on filters should be reduced. On the other, because filters which might not have been called previously will now be called even though they would never have processed the event previously, there will be more processing performed. It is felt that any change in the processing of these filters will be justified by the more consistent and deterministic behaviour of the filters.
|