Run-time (re)configuration

Figure 11. vjConfigChunkHandler interface

All VR Juggler managers support run-time reconfigurability. Input devices can be reconfigured at runtime without affecting the current application. A reconfiguration request can be sent to the input manager requesting that a specific proxy be pointed at a different device. Displays can also be added and removed at run-time. This feature is particularly useful for multi-screen VR systems, because screens can be activated and deactivated as the application runs. Applications can also receive configuration information. This allows applications to respond to changes at run-time in the same way that the rest of the VP does.

Config chunk handler interface

VR Juggler provides a common framework for processing configuration and reconfiguration requests (seeFigure 11). The framework defines an interface (vjConfigChunkHandler) that specifies the methods a "handler" component must implement to support run-time reconfiguration. All reconfigurable objects must conform to this interface. New objects can add support for run-time reconfiguration by simply inheriting from the vjConfigChunkHandler class and providing implementations for the methods defined by the interface.

The methods in the handler interface are:

bool configCanHandle(vjConfigChunk* chunk):

This function is used to determine whether the handler object knows how to handle the configuration chunk given. If the object wants to handle the chunk, then this method should return true. If not, then it returns false.

bool configAdd(vjConfigChunk* chunk):

The system is requesting that the handler reconfigures the system to add the configuration specified by the given chunk. It returns true if the change is successful, and false otherwise.

bool configRemove(vjConfigChunk* chunk):

The system is requesting that the handler reconfigures the system to remove the configuration specified by the given chunk. It returns true if the change is successful, and false it the change is unsuccessful or cannot be completed.

The class interface creates a simple interface for allowing objects to become configurable. Since all configurable components support a common interface, the system can simply keep a list of reconfigurable components. It is not necessary to know the actual type of the components that are being configured.

Pending configuration queue

Figure 12. Configuration manager structure

Reconfiguration is implemented using a pending config queue that is contained in the config manager (see vjConfigManager in Figure 12). The queue contains a list of all currently pending reconfiguration requests. As with all other managers, the kernel controls the configuration manager. The kernel is in charge of synchronizing the system and sending reconfiguration requests.

The system initially starts with only the kernel and several system managers instantiated. When the kernel receives a new configuration request, it passes it to the config manager which adds the config chunk to a pending reconfiguration queue. The request remains there until the kernel attempts to reconfigure the system with the pending requests. When a request is successfully processed, it is removed from the queue. If a request is not able to be successfully satisfied, then it remains in the queue until the system is able to satisfy the request.

Figure 13. Interaction of run-time configuration components

Each frame, the kernel checks for new configuration requests (see checkForReconfig in Figure 13). If there are any new entries, then the kernel attempts to process the request. The kernel steps through all known managers (that implement the vjConfigChunkHandler interface). The kernel asks each one in turn whether they can handle the configuration request. If they can, then the kernel passes the request on to the manager. The manager then processes the request using whatever methods it requires. For example, the input manager maintains an internal list of configurable devices. It looks for the device that corresponds to the given chunk, and configures it.

When the manager returns from processing the request, the kernel continues on to the next manager and attempts to process the same request. The kernel does not stop processing with the first manager that can handle the request, because multiple managers may need to respond to the same configuration requests.

After the kernel has iterated through all the known managers, it checks to see if any of the managers successfully processed the configuration request. If a manager successfully processed the request, then the kernel removes it from the pending queue. The configuration request is also stored in an active configuration data structure so the system may use the information again in the future. If no manager was able to complete the request, then the request remains in the queue where the kernel will try to process it again in a future frame.

This method of processing the configuration information is based on the chain of responsibility pattern [#design patterns GOF]. It differs only in that the handlers in VR Juggler do not have a strict successor chain. Instead, the kernel keeps a list of several base handlers. The kernel then traverses this list to find a valid handler for a given reconfiguration config chunk. These handlers may then in turn process the request in any way they wish. VR Juggler does not impose any specific behavior on the reconfigurable components.

It is also possible for a system component to directly query the config manager if an object would rather not have the kernel manage its reconfiguration.

Dependency management

Figure 14. Dependency checking classes

It is not difficult to image situations where the configuration of one component relies upon the successful loading and configuration of another component. For example, in VR Juggler users access all devices through proxies. The device proxy serves as a handle to a device that allows the system to access the device indirectly. Users configure proxies by specifying the device that the proxy should reference. If the system attempts to configure a proxy before the device being referenced has been loaded, the proxy’s configuration will fail. Any system that implements configuration or reconfiguration of the system must have a system in place to handle these inter-dependency issues.

Dependency checking

To handle dependency resolution, VR Juggler performs dependency checking between system components. Dependency checking refers to the process the system goes through when it runs a check to see if all the system resources required by a new configuration request are available before it allows that request to be processed.

Dependency checking is supervised by the vjDependencyManager. The dependency manager allows the system to send it a query about any configuration chunk. The dependency manager will check to make sure that any dependencies that need to be satisfied for the given chunk have been satisfied, and will return approval to the caller.

The dependency manager checks dependencies by maintaining a list of dependency checkers objects that it uses to check for dependency fulfillment. All dependency checker objects implement a common interface specified by the vjDepChecker class (see Figure 14).

This interface defines two methods that are used for dependency checking:

bool canHandle(vjConfigChunk* chunk)

This function is used to determine whether the checker object knows how to evaluated dependencies for the configuration chunk given. It returns true if the object knows how to evaluate the dependencies, and false if not.

bool depSatisfied(vjConfigChunk* chunk)

This function actually performs the dependency evaluation. If the dependencies are satisfied, then it returns true. If dependencies are not satisfied, then it returns false.

System components that require advanced dependency checking are responsible for registering their dependency checkers with the dependency manager. The VR Juggler manager that most directly deals with the components often handles this registration. For example, whenever a new device registers with the input manager, the device also registers any dependency checkers with the dependency manager.

The manager processes dependency requests by querying each dependency checker object to find out what types of configuration chunks it can evaluate.

When the manager receives a dependency query, it attempts to find a dependency checker that can handle the given configuration chunk (see Figure 13). If it finds a dependency checker, then it sends a request with the chunk to the checker. The checker then checks for whatever system state is required and returns an answer, which the manager returns to the originator of the query. If the dependency manager does not find a checker, then it uses the default dependency checker to check for many of the most common dependency issues.

Automatic unloading

One issue that we have not touched upon yet is what to do if a user removes a component from the system through reconfiguration, but another component relies upon the removed item. As an example, consider again the case of a proxy that is configured to reference a given input device. After the system is fully configured, the device is removed via reconfiguration. How does the system keep the proxy in a valid state? How does it re-connect the proxy if the original component is later re-added to the software system?

VR Juggler handles these issues through "smart unloading" of system components. When a component’s dependencies are reconfigured or removed, smart unloading allows the dependent component to reconfigure itself dynamically in an attempt to satisfy its dependencies by modifying its behavior. If the dependencies cannot be satisfied by dynamic modification, then the component is unloaded and placed back into the pending queue. This effectively backs-out the original configuration requests until all the dependencies are once again satisfied.

Because this process could lead to a cascade of unsatisfied dependencies, it continues iteratively until all components of the system are once again in a stable state.

This area of the system is a current area of active research. We are investigating better ways for components to dynamically change the way that they work in an attempt to deal with unsatisfied dependencies via graceful degradation. We are also working to better techniques to deal with some of the more highly complex dependencies.

Run-time reconfiguration discussion

Results of the initial implementation have been very positive. We have been able to create a flexible architecture that allows for the majority of system components to be reconfigured at run-time. We are still refactoring the design to increase flexibility and add more reconfiguration abilities to the system.

The reconfiguration system has been used to create application "switchers" at VRAC. When we give tours of the VR systems at VRAC, we are faced with the problem of needing to rapidly switch between applications and VR system settings. Before we had run-time reconfiguration, we would have to shutdown the application and the VR system every time we wanted to switch applications. This led to unacceptable downtime and increased risk of potential problems when restarting the application.

Using run-time reconfiguration, we have been able to solve this problem by creating switcher applications. Our switcher applications provide a single unified virtual environment that contains and manages any number of stand-alone user applications. Once the switcher application has been started, users of the VR system can quickly swap between the stand-alone applications without having to leave the switcher virtual environment or change system settings. This allows people giving tours to just start a single application that allows them to run any number of other applications within the same environment.

We have also benefited immensely from the increased robustness that the reconfiguration system affords while debugging new hardware systems and drivers. Because of the reconfiguration system, the applications keep running even when the hardware fails and has to be reconfigured or entirely restarted.

Run-time reconfiguration has also allowed us to do run-time performance tuning using the abilities provided by VR Juggler. VR Juggler provides a performance monitoring system that allows users to interactively evaluate the performance of their applications [#Juggler performance Just IPT 2000]. Using this tool, developers can pinpoint performance bottlenecks. Then using run-time configuration they can make changes in an attempt to increase performance.

The implementation of run-time reconfiguration has not been without its problems though. There have been several obstacles we have had overcome and work around during the implementing the system.

One area of difficulty that we have found is that some of the popular application programming interfaces (APIs) used in a VR application do not lend themselves well to reconfiguration. Some graphics APIs (such as Iris Performer) expect the be in complete control of system resources and as such do not provide a way to release the API’s resources when VR Juggler does not need them any more. We are actively pursuing ways to alleviate these problems by communicating with API developers and by investigating systems that are more flexible.

An unexpected result was that average VR users did not jump into using run-time reconfigurability immediately. Dynamic systems are still a new concept and it will take some time for users to become comfortable with and begin actively taking advantage of the new abilities afforded by such a system. As we are training out users and show them the benefits of reconfigurability, the are becoming increasingly excited and have started to use reconfigurability in their applications.