• No results found

A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away. — Antoine de Saint-Exupery

Thecmccomponent model and framework islightweight, and does not restrict the host programming language at all. Components share memory as they see fit, and the communication is realized directly by the native method invocation means of the host language. This helps to maximize the efficiency of the model checker, but makes the component model quite unsuitable for reconfiguration: first, since parts of the component’s state are shared (reflected by the σ part of the state in the component model), removing a component would require an intricate (or user- supplied) detection of the memory that can (and must) be freed, and second, due to the maintenance of a stack, replacing a component might result in the necessity to operate both the old version (for processing of pending stack elements) and the new version for some time.

It hence became interesting to investigate aheavyweight component model that imposes restrictions on the component’s code by disallowing shared memory and call stacks. Components are forbidden to communicate by any other means than those provided by the framework, making the communication completely observ- able. Such a rigid framework is required for reconfiguration; if the framework lacks control on its components, it can hardly prevent errors emerging from a reconfigu- ration done in the wrong moment.

All this is paid for by a severe loss in efficiency. Enforcing the absence of shared memory contradicts the provisions of most host languages, but it is required to avoid “covert channels” that components might use to circumvent the framework- provided communication means. But efficiency is of little concern here: instead, we strive to define a component model that is as simple as possible to ease the reasoning about reconfiguration. In this chapter we present theJCompcomponent model. We first investigate the requirements for a component model capable of supporting reconfiguration, explain the design principles we applied, and introduce a formal component model. By extending this component model, we introduce a formally described way to conduct reconfiguration, and show some of its properties. The implementation of theJCompcomponent model will be described in the next chapter.

6.1. Requirements

A component model is usually devised to support a specific class of applications. The specific properties of the component model – how communication is to be conducted, how components are to be represented in memory – then follow the requirements imposed by that class of applications. For example, if multimedia is to be supported, special media streaming connectors need to be provided [SvdZH08]. Reconfiguration is then included in a way that supports the retainment of these

properties over the reconfiguration process: if the media connectors need to provide some latency control, reconfiguration needs to be designed in a way such that the latency bounds are not violated during reconfiguration [MNCK99].

Reconfiguration is, in principle, a vague term. It can be used to describe the restarts that SAT-solvers do [BLMS01] or intricate morphing of the application structure towards a goal. Depending on the component model and the required properties, reconfiguration may yield different problems. For example, multimedia reconfiguration needs to preserve real-time constraints, which is why the Djinn

framework interleaves reconfiguration as much as possible [MNCK98,MNCK99]. This requires a delicate data-flow, as the reconfiguration is entirely non-atomic and parts of the data might be processed by the new component while data still processed by the old component is found in the system. For providing this, Djinn only considers the switching of component chains, i.e., the rerouting of data flow. Quite differently, hot code update does not need strong realtime properties. Instead, the preservation of the components’ state is much more important. For example,

Draco[Van07] puts much emphasis on how a mapping between the old and new

components’ state can be derived. But for Draco, reconfiguration is limited to single components only, and thus does not impose much of a structural problem.

In this thesis, our interest is somewhere in between. In considering reconfigura- tion as a valuable tool for realizing adaptivity, we try to allow as much as possible as a reconfiguration. However, in the absence of a application scenario requiring their consideration, real-time aspects are ignored, and performance as an abstract idea is only considered in that the reconfiguration tries to be minimal: Instead of blocking the entire system during reconfiguration, only the relevant part is blocked, with the remainder being allowed to continue its operation.

In this section, we will formulate requirements we consider important for a component model capable of realizing this concept of reconfiguration. We have discussed situations where reconfiguration can be applied on page21in Sect.2.4.5.2, and provided a taxonomy of features in Sect. 3.2. Also, we have discussed some aspects of reconfiguration in Sect.2.5.1. Finally, we have discussed the influence of thecmccase study on our reconfiguration approach in5.7.1.

First, in order to conduct reconfiguration, the component model must yield access to the required properties of the components. Being interested in stateful components and component setups as described in Sect. 4.3, we formulate the following requirements on a component model capable of reconfiguration:

R1.1 The component model must make it observable if a component can be reconfigured at a given point in time. We do not want to impose that reconfiguration can be conducted at any point in time, as components might be required to finish some tasks first, but it is necessary to determine whether reconfiguration can be conducted or whether further waiting is necessary.

R1.2 The components’ connections must be readable and modifiable. During reconfiguration, the connections of existing components are modified, and the component model must guarantee that this is indeed possible. R1.3 Components can be created and removed. Reconfiguration might intro-

duce new components, and discard old components no longer required. The component model must support these addition and removal of com- ponents.

R1.4 The component state must be readable and writable. Being interested in stateful components, we demand that the component model supports ac- cess to the relevant state data. Also, this data must be sufficiently enriched with information such that the reconfiguration algorithm can handle it as

required by the user. For example, if the user wants to retain some mes- sages sent by component A to component B over role r, then messages need to contain information about their source component and role, in order to judge whether the user’s request applies to a given message. These requirements need to be fulfilled by a component model that supports state- ful reconfiguration. Most component models will either support these requirements already, or be easily modifiable (e.g., requirement R1.4 will most likely lead to some changes in the component’s state, if this is discussed within the component model). However, our interest goes further, and ultimately we want to prove properties of our reconfiguration approach that lead to guarantees given for the actual implemen- tation. In order to transgress from the model to the implementation, we impose two requirements on the reconfiguration algorithm:

R2.1 Reconfiguration must be conducted by a sequence of conceivably atomic steps. Instead of just describing the overall effect of the reconfiguration plan, the reconfiguration algorithm needs to be described in a way that can be directly implemented in a regular programming language. As such, we require the reconfiguration algorithm to be implemented by a sequence of fine-grained steps.

R2.2 Reconfiguration must follow a plan with a precisely defined semantics. In order to reason about the effects of reconfiguration, the reconfiguration input must be constrained. We hence require that the reconfiguration algorithm operates on a plan, which fully describes the reconfiguration (opposed to just providing a number of primitives for reconfiguration, and pushing the responsibility to use them in a consistent way onto the user).

This results in a small-step semantics for the reconfiguration plan. The approach of using term rewriting to define the semantics of the component model integrates well with such a requirement, and we are unaware of a component model that has a sufficiently fine-grained semantics to support requirement R2.1.

The implementability of the reconfiguration algorithm is used for obtaining guarantees in the framework implementation that can be proven on the component model. Based on the literature presented in Chapter 3, as well as the experience obtained with cmcand the theoretical considerations of Chapter2, we require the following guarantees, which need to be ensured by the reconfiguration algorithm:

R3.1 The results of a reconfiguration must be predictable. That is, if a given re- configuration plan is conducted at a known state of the component setup, the outcome should be as deterministic as possible. This requirement can be refined into two sub-requirements:

R3.1.1 The reconfiguration process should appear as atomic. Even if the reconfiguration algorithm is conducted by a sequence of fine-grained steps (cf. requirement R2.1), none of the components involved should ever notice that a reconfiguration is underway. This applies to com- munication especially: no communication must be disrupted or lost due to an ongoing reconfiguration.

R3.1.2 The reconfiguration should interrupt components only at predictable points. Since we are interested in state retainment, we need to have some guarantees about the point in time where reconfiguration in- terrupts ongoing computations of the component. If reconfiguration might interrupt a component at any given moment, little assump- tions could be made about the data state at the point in time where reconfiguration commences.

Both these requirements provide a guarantee to both the component im- plementer, who can remain mostly unaware of reconfiguration scenarios, and the system designer, who can rely on reconfiguration not interfering with ongoing computation.

R3.2 Reconfiguration should be minimally invasive. The reconfiguration ap- proach offered by the component model should guarantee that only those components that are strictly required to be modified or temporarily halted during configuration are affected. For large-scale, distributed systems, re- configuration cannot be allowed to block the entire system to achieve atomicity (for satisfying requirement R3.1.1). Instead, the system should be kept alive as much as possible, allowing components to continue their operation, even if this entails communicating with components under re- configuration.

R3.3 A component’s implementation must be allowed to stay ignorant of re- configuration. As with the requirements of R3.1, we want to have the component model ensure the component implementer that reconfigura- tion can be conducted with components that have not been especially prepared for being reconfigured.

We would like to require two more guarantees, but they are difficult to formulate, as they depend, to some extent, on the component setup at hand. We hence formulate them as “wishes”, and require that the component model does not impede them, should the component setup itself be compatible:

R4.1 Reconfiguration should be conducted as soon as possible. Obviously, we required that reconfiguration waits for predictable points to be reached within a component’s execution (requirement R3.1.2), and we required these points to be observable (R1.1). But other than that, if reconfigu- ration is requested, the model should not delay the reconfiguration. But components might behave in a way that inhibits reconfiguration forever. R4.2 The reconfiguration algorithm should guarantee that a reconfiguration plan

is realized correctly. A reconfiguration is planned to affect some change on the system, and the state of the system after the reconfiguration is en- visioned by the reconfiguration designer. The reconfiguration algorithm should strive to meet these assumptions about the state after the reconfig- uration; especially the system should not experience unexpected deadlocks during reconfiguration.

In combination with requirement R2.1, the guarantees we require from our compo- nent model are rarely discussed. Some of the guarantees are quite commonplace in the literature, e.g., the necessity to conduct reconfiguration in a way that makes it appear as atomic (cf. the third column of Tab. 3.1). But these guarantees are usu- ally not proven on a small-step granularity, which we wish to do in this thesis. For conducting such formal proofs in the presence of requirement R2.1, the component model has to be given a rigid semantics, even without considering reconfiguration. Therefore, we require a model which fixes communication as much as possible. Clos- est to realizing the requirements while discussing reconfiguration implementation on a comparable granularity is NeCoMan [JDMV04], but it focuses on a very distinct application area (cf. Sect.3.2.27.5).

In this thesis, we address a general component model that is to support a variety of applications. We do not formulate specific requirements here, but require the component model to be suitable for implementing applications that process data, while not requiring frequent user interaction. Contrary to the cmc model, we do not impose efficiency requirements, however. The supported reconfiguration,

however, should allow for arbitrary changes to the component graph, and not be limited to specific patterns.

6.2. Design Principles

JComp is a component model that is dedicated to the research of reconfig-

uration. It strives to implement the requirements stated above as concisely as possible, omitting many features found in other component models. This helps to satisfy requirement R3.1, as additional features (e.g., customized communica- tion means) have the tendency to make the result of a reconfiguration less easy to predict. Some requirements (R1.2, R1.3, R3.2) suggest a component model where components are inter-tangled as little as possible, both in means of shared memory and communication. These requirements are incompatible with the cmc model checker’s component model, which uses call stacks and shared memory (cf. the dis- cussion in Sect. 8.5). Instead, we found these requirements similar to those for a component model capable of arbitrary distribution (e.g., any component setup must be distributable on a number of machines with an arbitrary assignment, with- out changing the semantics of the system). In imposing the requirements for doing cross-machine communication on all components, two basic design principles got defined:

(1) JCompuses message passing as the sole means of communication between components,

(2) applications devised with the JComp component model should be dis- tributable without any changes to the components’ code.

Interestingly, the resulting JComp framework never really excelled in having ap- plications distributed (mostly because we lacked a proper example scenario) and was eventually even modified to only support distribution on the user level, as dis- cussed in Sect.7.3. Nevertheless, the implications of the two aforementioned design criteria resulted inJCompbecoming rather strict with respect to the design of the components, which in turn enforced the clean separation of components required for clean and predictable reconfiguration.

Being able to distribute a component-based application (i.e., transparently in- stantiate components on different machines) requires the components to be inca- pable of using the fact that they operate on the same machine (which minimizes their entanglement). First of all, this means that no shared memory must be used. If two components access shared memory, they cannot be put on different ma- chines (without changing the semantics or without tedious synchronization of the machines’ memories). Hence, the first derived design principle for JCompreads:

é Components must not share memory.

Of course, this also applies to other shared resources like the file system, but we will only discuss memory here. If no shared memory is allowed, the only way one component might influence another component is by explicit communication. This is appreciable, as it enforces one of the basic properties of component-based systems, namely observability of communication (cf. Sect.2.1), which we will eventually use to satisfy both requirements R2.1 and R3.1.1 as well as guarantee requirement R3.1.2.

The first design principle – message-based communication – requires that a caller component runs in a different thread as the receiver. Actually, it does not technically require that (there may be a non-preemptive scheduling that runs one component at a time), but on the conceptual level, this situation is mandatory:

Concurrency has many well-known dangers, like race conditions, which pose a threat to requirements like R3.1.1. But since all communication is done explicitly between components, racing can only occur for message order. Usually, this can be dealt with. This absence of shared memory greatly facilitates the concurrency of com- ponents, because an important invariant is provided: During the execution of a method, none of the accessible data is modified concurrently. This amounts tolocal non-concurrency or mono-threaded components. The component implementer is assured that, for the duration of a message processing, no component-local data (which is all the data the component has access to) is modified concurrently. Re- quirement R3.1.2 (when choosing methods as the basic unit of atomic processing) extends this guarantee to reconfiguration: if a component was able to complete a method’s execution in a non-concurrent setup, neither concurrent components nor reconfiguration can interfere and keep it from terminating the method’s processing. Message passing works by delivering messages to components, where they be- come queued. Sometimes, we might desire a method result, or wait for some method to complete, and thus requiresynchronous messages, i.e., messages that block the calling thread until the message has been processed. Only for synchronous mes- sages, a result value can be provided, although the concept offutures provides sort of a compromise [BH77,CH05,CHM08]. So we decided to include synchronous messages. There is, however, a problem involved: The problem of synchronous callbacks. ComponentAsends a synchronous message to componentB, and waits for its processing to end. During the processing of this message,B decides it needs further information from Aand sends a message toA, waiting for this message to be processed. Now, the system deadlocks, asA is currently waiting forB to finish processing of the initial message and thus incapable of processing the message just received.

There are two straightforward solutions to this problem: The first one is to just accept that deadlock as an error and disallow synchronous callbacks. The second one is to provide means to process that callback, i.e., by forking a thread. For

JComp, we decided to take the first approach, even if this seems too rigorous: The

second approach invalidates the invariant of not having the memory changed during the course of a method, an invariant that is otherwise provided by the components being mono-threaded. Obviously, this is not as troublesome as genuine concurrent modification, as any such modification will happen while waiting for a synchronous

Related documents