The concurrency model used when implementing the application model within the Reaction framework is well defined. All callback execution is carried out within the context of the main reactor thread, with long running tasks being farmed out to a worker thread pool and completing via the standard callback mechanism. This essentially eliminates the need for synchronisation, since all object state updates which rely on callbacks will execute atomically within the main reactor thread.
Although the core application code can take advantage of this clean concurrency model, complications are introduced the Reaction framework is used in conjunction with an independent GUI event loop. In order to address these problems, there are some common techniques which are useful when implementing the model's interface to the view and controller components.
One of the main complicating factors when implementing the control interface on the model is synchronisation between the user interface and the model state. Since the controller part of the user interface will typically be running in an independent thread there is ample scope for introducing race conditions and potential deadlocks. In order to mitigate these problems it is advisable that the control interface on the model adopts the following rules:
It is relatively easy to achieve these objectives by making use of the Reaction framework's signal event functionality. A key property of signal events are that they can be triggered from any thread context and will then issue signal callbacks within the context of the main reactor thread. By way of example, consider a simple `stopwatch' example that provides a control interface with timer start, stop, reset and quit options. The Java interface definition for such a control interface would look like the example shown in Listing 9.1.
In order to make the control interface safe to call from any thread context, some mechanism of handing the API call from the GUI thread to the reactor's main thread is required. To achieve this each API call can be mapped to a signal event, with the application logic being implemented in the signal event handlers. The use of Java generics allows the signal event data parameter to be strongly typed, and this will usually map to an immutable data type which wraps the API call parameters for propagation to the appropriate signal event handler. In the case of the example, a simple enumeration may be used to encode the nature of the API call, as shown in Listing 9.2.
Given a suitable signal event parameter for each API call, it is then possible to dispatch the calls via a signal callback, as illustrated in Listing 9.3. Note that this example is simplified by mapping all of the API calls to a single signal. In more complex applications, multiple different signal objects may be used in order to increase modularity.
Once the control interface API calls have been converted to signal events they can be handled via a suitable signalable object which is registered to receive the generated signal callbacks. The onSignal callback method of such a signalable object may be seen in Listing 9.4.
In this example, the commandId value which is passed as the signal parameter is used to differentiate between the different API calls. This allows the appropriate control action to be carried out from the context of the main reactor thread, eliminating the need for additional synchronisation. Note that a null controlId parameter is used to imply signal finalization on application shutdown.
This general pattern of using signal events to decouple control-side API calls from updates to the model state should be employed by the model component for all control interfaces. However, there is still considerable flexibility in terms of specifying more complex signal parameter data objects or using multiple signals to partition the control interface functionality.
An approach to implementing a viewer interface using the Publisher-Subscriber pattern has already been introduced as part of the discussion of signal events in Section 4.4. In short, this relies on the application model component providing a suitable update notification signal and a range of getter methods which expose the current state of the published data. In the case of the simple stopwatch example shown in Listing 9.5, this implies a single getter method which returns the current elapsed time.
When implementing a viewer interface to the core application model, it is necessary to make certain assumptions about the types of viewer component which are supported. These assumptions lead to the following general rules which should be followed when implementing the viewer interface:
A key aspect of these assumptions is that the viewer component carries out all rendering for a given update within the context of the update signal notification callback. The implication of this is that the model view cannot then be modified while the viewer is actively rendering the view data.
In certain circumstances it may not be viable to carry out all rendering within the update notification callback - for example, when rendering to remote client via a framework such as GWT. However, it is the responsibility of the viewer component to deal with such issues, not the core application model.
Typically, GUI frameworks which support delayed rendering will need to read back the view data in the context of the update notification callback and then cache it for future rendering. In order to support this, all data returned from view interface getter functions should be immutable, using `copy on read' for non-native objects. Where this is not practicable, this should be explicitly stated in the interface contract.