For Coordination, State Component Transitions
When building large concurrent systems, one of the key difficulties lies in coordinating component behaviour and, in particular, concurrent access to resources. Native mechanisms such as, for instance, locks, semaphores and monitors allow developers to address these issues. However, such solutions are complex to design, debug and maintain. Indeed, coordination primitives are mixed up with the functional code, forcing developers to keep in mind both aspects simultaneously. Finally, in concurrent environments, it is difficult to envision all possible execution scenarios, making it hard to avoid common problems such as race conditions.
The coordination problem above calls for a solution that would allow developers to think on a higher abstraction level, separating functional and coordination aspects of the system behaviour. For instance, one such solution is the AKKA library implementing the Actor model. An actor is a component that communicates with other components by sending and receiving messages. The processing of a message by an actor is atomic. The state of an actor cannot be directly accessed by other actors, avoiding such common problems as data races. However, component coordination and resource management are still difficult. Fairly complex message exchange protocols have to be designed, which are still spread out across multiple actors. Any modification of the coordination policy calls for the corresponding modifications in the behaviour of several actors, potentially leading to cascading effects and rendering the entire process highly error-prone.
Our approach relies on the observation that the behaviour of a component can be represented as a Finite State Machine (FSM). An FSM has a finite set of states and a finite set of transitions between these states. Transitions are associated to functions, which can be called to force a component to take an action or to react to external events coming from the environment. Such states and transitions usually have intuitive meaning for the developer. Hence, representing components as FSM is a good level of abstraction for reasoning about their behaviour. In our approach, the primitive coordination mechanism is the synchronisation of transitions of several components. This primitive mechanism gives the developers a powerful and flexible tool to manage component coordination. Furthermore, this approach allows us to clearly separate the behaviour of a component and the interface it exposes for the interaction with other components from the system-wide coordination policies.
OSGi associates to each bundle a simple state machine representing the bundle's lifecycle. A bundle can be in one of the states Installed, Resolved, Active, etc. However, once the bundle has been started, it remains in the state Active — the functional states are not represented. Therefore, this mechanism is not applicable for coordination of active components.
We have implemented functional component coordination in OSGi by using BIP coordination mechanisms (see also the overview on the RiSD web site). BIP is a component framework jointly developed by Verimag (France) and EPFL RiSD (Switzerland) laboratories lead by Prof. Joseph Sifakis (2007 Turing Award laureate). In BIP, systems are constructed by superposing three layers of modelling: Behaviour, Interaction, and Priority. The first layer, behaviour, consists of a set of components modelled by FSM. The second layer models interaction between components defining explicitly which transitions could be synchronised. When several interactions are possible, priorities can be used as a filter. Interaction and Priority layers are collectively called glue. The execution of a BIP system is driven by the BIP Engine applying the following protocol in a cyclic manner:
Upon reaching a state, each component notifies the BIP Engine about the possible outgoing transitions;
The BIP Engine picks one interaction satisfying the glue specification and notifies all the components involved in it;
The notified components execute the functions associated to the corresponding transitions.
To use the transition synchronisation mechanism, developers must ensure that the component states remain stable during one cycle of the above protocol: a component must be able to perform any transition it has announced as possible to the BIP Engine.