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 assigned a unique integer command identifier, as shown in Listing 9.2.
Given a unique identifier for each API call, it is then possible to dispatch the calls via a signal callback, as illustrated in Listing 9.3. Note that when setting up the signal parameter data, the command identifier is specified as the first item in an object array of arbitrary size. This allows parameters passed as part of the API call to be passed via the signal mechanism by appending them to the object array.
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 opaque data object passed as the signal parameter is known to be an object array with the first entry specifying the API call made. 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 data 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.