Performing an action at the end of a group (or any other compound action) may be difficult if the delays of the group’s actions are expressions or if some conditional constructs are involved. Even with constant delay and no control structure, computing the duration of a group, or a loop/whenever/forall body, can be cumbersome.

This observation advocates for the introduction of two additional sequencing operators that are used to launch an action at the end of the preceding one:

(no special name) followed-by ended-by
a b a ==> b a +=> b
b is launched together with a b is launched at the end of a b is launched at the end of a and its eventual children

The juxtaposition (launch synchronously/in parallel), the followed-by operator (launch at the end) and the ended-by operator (launch at child ends) are binary operators called continuation combinators. Delays are not continuations1: they are unary operators applied to an action to defer its start.

The effects of the three operators are illustrated by the figure below. The end of a compound action is represented by the bold outlined rectangle. The child actions may end earlier or later than the end of the top-level actions:

illustration of the continuation combinators

Continuation combinators do not change the scope of the local variables of their arguments. In other words, in a ==> b the actions in b cannot access the local variables defined in a.

Continuation combinators freely compose between actions and are right associative. Here are some examples:

Expression Meaning
a ==> b ==> c is equivalent to a ==> { b ==> c } and specifies that b ==> c starts at the end of a
{ a ==> b } ==> c starts c at the end of { a ==> b}, that is, with the end of b
{ a ==> b } c starts c with the start of { a ==> b }, that is, with the start of:::antescofo a`
a ==> b c is equivalent to a ==> { b c } and starts { b c } with the end of a
a b ==> c is equivalent to { a b } ==> c and starts c with the end of { a b }, that is, the end of b
a +=> b ==> c is equivalent to a +=> { b ==> c } and starts { b ==> c } at the end of ‟a and its children”

For instance, suppose we want to make an action after the end of a loop:

          $cpt := 0
          Loop 1 
                print "tic" $cpt
              3 print "tac" $cpt
                $cpt := $cpt + 1
          } during [3#]
          +=> print "loop ended"

Here there will be 3 iterations of the loop. So, if the loop starts at date 0, the first iteration starts at 0 and ends at 3, the second one starts at 1 and ends at 4 and the last one starts at 2 and ends at 5.

Instead of explicitly computing these numbers to launch an action at the right time, we have used the continuation combinator +=> which waits the end of the loop and all the loop bodies, to trigger the print message: the message "loop ended" will appear at date 5.

As you can see, the end of a loop is different from the ends of the loop bodies: the loop in itself terminates when the last iteration is launched. As a matter of fact, the computation associated to a compound action a can be seen as a tree, with the sub-computations rooted at a. Thus, there is no need to maintain a after having launched the last sub-computation. So the end of a is usually not the same as the end of the last sub-computation spanned by a and this is why the operator is usually more handy than ==>.

Nevertheless, the end of an action is always precisely defined altough it can be only dynamically known:

Continuation and abort

Abort handlers launch a group at the premature end of a compound actions. So they differ from the followed-by and ended-by operators that launches a group of actions at the (natural or premature) end of an action.

An abort handler, specified by the @abort attribute, is considered as a child of the associated action. So, when an abort handler exists and the associated action is aborted, the abort handler is launched with the followed-by continuation (if it exists). Because the abort handler is necessarily defined before, it happens before the followed-by continuation.

The ended-by continuation is launched after the end of the abort handler (because the abort handler is a child).

Continuations are not considered children of the continued action. So in a ==> b, b do not has access to the local variables of a, contrary to the @abort clause of a.

For example (note the bracketing of the process call):

          @proc_def ::P()
          @abort { print abort P $NOW }
               print start P $NOW
            10 print BAD END P $NOW

          { ::P() ==> print continuation P $NOW }

          print "launch abort" $NOW
          abort ::P

will give the following trace:

      start P 0.0
      launch abort 5.0
      abort P 5.0
      continuation P 5.0

The continuation is launched at date 5.0 because it is launched with the end of the instantiated process (here a premature end caused by the abort command). If the abort handler is replaced by:

          @abort { 11 print abort P $NOW }

the corresponding trace is:

      start P 0.0
      launch abort 5.0
      continuation P 5.0
      abort P 16.0

because the followed-by continuation does not wait the end of the abort handler. If we replace the followed-by continuation by an ended-by continuation

          { ::P() +=> print continuation P $NOW }

the trace becomes:

      start P 0.0
      launch abort 5.0
      abort P 16.0
      continuation P 16.0

because the continuation takes place at the end of all of the action's children, including the abort handler.

  1. Because a delay can be used in front of the first action of a sequence, a delay does not necessarily links two successive actions.