The semantic model of an HLPN can be described in several ways. An HLPN is normally represented using
a graphical form which allows visualization of system dynamics (flows of data and control). We have decided to keep the internal representation of a workflow using the Petri Net.
The Object Oriented representation of a Petri Net is depicted in Fig. 1. We use parametric programming in order to represent type dependencies which are implicit in the HLPN model.
In our design we do not allow edge expressions as far as an edge expression can be represented by a transition which performs the same operation.
|
Fig. 1 |
A Petri Net is composed by a set of places (which have a type), transitions and edges. Edges have also a
type which is the type of the incoming or outcoming place. Tokens are also typed as far as are stored into places. Transitions can have a
boolean condition and an
operation. Operations have only one return type and their signature are represented by the parametric Function object which define the type of the formal parameters and the return type of an operation.
The main goal of the proposed Petri Net-based engine is to obtain an high parallelism in executing transitions, assuring state consistency and keeping the overall design simple. The dynamic behavior of the engine is herein described using a finite state machine.
One of the main problem in implementing the semantics of the Petri Net model in a imperative paradigm provided by the mainstream programming languages (such as C/C++ and Java) is the non-determinism.
Our implementation solves the problem by (i) synchronously firing one transition at a time, and by (ii) selecting one of the possible transitions randomly.
Thanks to a proper firing mechanism is also possible to emulate parallelism and garantee state consistency.
Fig. 2 depicts how the firing of a transition is handled by the engine; it is divided into 3 phases.
- Phase 1*: tokens are moved from the input places (p1) and bound to the input variables (x) of the chosen transition (T1).
- Phase 2*: the transition is fired: the associated operation (f(...)) is performed using the token values stored into the input variables (x) and the corresponding result is stored in the output variables (y).
- Phase 3*: output tokens are moved from output variables (y) to the respective output places (p2) of the
transition.
|
Fig. 2 |
In order to preserve state consistency all the 1* and 3* phases need to be executed in a mutually exclusive way. The execution of such phases is nevertheless negligible if compared to the Phase 2* which, in the workflows application domain, usually consists in the interaction with remote services (e.g. a Web Service). As far as Phase 2* does not interact with the Petri Net state, high parallelism (ideally infinite) is obtained by executing transition operations on separate threads.
The engine behavior is described by the single-threaded State Chart diagram in Fig. 5.
|
Fig. 3 |
The engine fires enabled transitions (one-by-one) until all pending operations and enabled
transitions are processed. When no longer transitions are enabled the engine returns the
final marking of the network as the result of the workflow.