Coordination¶
Usually not all possible role-playing is actually allowed. There may be some
static constraints, e.g., “role a
and role b
must not be played at the
same time”, or temporal constraints, such as “role a
must be played before
role b
can be played”. In order to monitor and constrain role-playing in the
model, one or more role-playing coordinators can be defined. A coordinator is
basically a special component, which can not only synchronize over actions, but
also over role-playing.
Role guards¶
Consider the following example containing roles a
and b
:
natural type N {
s : [0..2] init 0;
[act] s < 2 -> (s' = s + 1);
}
role type R(N) {
t : [0..1] init 0;
[act] t = 0 -> (t' = 1);
}
system {
n : N;
a : R; a boundto n;
b : R; b boundto n;
}
In order to enforce the first constraint (a
and b
must not be played
together), the following coordinator can be added:
coordinator {
[act] [!(a & b)] true -> true;
}
A coordinator command can contain an additional role guard after the action
label. A role guard is a Boolean expression over role instances defined in the
model. Intuitively, the command synchronizes both with the action act
and
all role-playings satisfying the role guard. Therefore, the transition where
both a
and b
are played at the same time is blocked.
The following table lists all forms a coordinator command can have.
Command |
Effect |
---|---|
|
Synchronize both with |
|
Synchronize with role-playing for all actions |
|
Synchronize only with |
|
Internal action, no synchronization |
Similar to regular components, a coordinator can have local state. This allows
us to specify the second constraint (role a
must be played before role b
can be played):
coordinator {
played_a : bool init false;
[] [a & !b] true -> (played_a' = true);
[] [!a & !b] true -> true;
[] [a & b] played_a -> true;
[] [!a & b] played_a -> true;
}
Here, we set the variable played_a
to true
once the role a
has been
played. Then, we only allow playing of b
if played_a
has been set. The
second command is necessary to allow transitions where neither role is played.
Explicit role alphabets¶
The alphabet of a role guard is implicitly defined by the roles it contains. It is possible to make the alphabet explicit, which allows us to collapse the last two commands into one:
[] [b over [a, b]] played_a -> true;
The alphabet is defined as an array of roles after the over
keyword. The
role guard shown above is evaluated over the roles a
and b
. Since it
only states that b
must be true
, a
can be both true
or
false
. Hence, this command has the same effect as the last two commands in
the original coordinator.
If all commands of the coordinator should have the same alphabet, the alphabet for the whole coordinator can be defined as follows:
coordinator over [a, b] {
// ...
}
Coordinator partitioning¶
There is an important difference in the semantics of coordinators and regular modules. A coordinator is automatically partitioned into independent parts. Two coordinator commands are independent if both their role alphabets and their updated variables are disjunct. Independent commands can be executed together, i.e., they can synchronize. Consider the following example:
coordinator {
[act] [a] true -> true;
[act] [b] true -> true;
}
Note that the commands of this coordinator are independent. Their role guards
have disjunct alphabets and none updates any variables. Since both are labeled
with the act
action, they will synchronize. Thus, the above coordinator has
the same effect as the following coordinator:
coordinator {
[act] [a & b] true -> true;
}