Pattern 3 (Synchronization)

Synchronization sees multiple branches converging into a single thread of control.

Concurrence expressions split the flow in parallel branches, children are branches. Attributes of the concurrence expression are used to define how synchronization should occur as each branch 'replies' at the end of the concurrence.


    <concurrence
        sync="generic"
        count="x|*" 
        over-if="x == y"
        merge="first|last|highest|lowest"
        merge-type="mix|override"
        remaining="cancel|forget"
    >
        (...)
    </concurrence>

This snippet is enumerating the attributes of the 'generic' sync expression and their potential values. The attributes of the parent concurrent expression are passed to the sync expression.

count

The count attributes, when omitted, defaults to the '*' (star) value, which means that the sync expression commands to wait for all the branches. When an positive integer is given as its value, it means that the sync will wait until the given number of branches (children) have replied, and then it will order that the concurrence (or concurrent-iterator) expressions replies to its parent.

Thus when 'count' is set to '1', we are facing a 'discriminator pattern' implementation. When the count is superior to 1, the 'N out of M pattern' is in the game.

'count' defaults to '*'.

over-if

This attribute appeared in OpenWFE 1.7.2pre0. Its content is evaluated each time a branch replies. If it yields 'true', the synchronization will be considered as over and the remaining branches (the ones that have not yet replied), will be cancelled (or forgotten if the 'remaining' attribute is set to 'forget').

The content of this attribute is a string expression similar to the one founds in the 'test' attribute of the 'if' expression (see the section called “case”).

merge

Merge indicates which branch (child) shall win the priority game of merging workitems. 'first' means that the first child to have replied shall see its workitem take precedence. 'last' is easy to guess. 'highest' means that, of the child that have replied, the highest in the list of the children will see its workitem have the priority.

The default merge tactic is set to 'MG_FIRST'.

merge-type

When 'override' is set here, the workitem that has the priority completely overrides other workitem. When 'mix' is in action, workitem fields are considered one after the other and the resulting workitem is composed by starting with the lowest priority workitem fields until the highest priority, the override occurs at field level.

remaining

'cancel' means that the children that have not replied when the sync is over (in the case of the 'discriminator' and the 'N out of M' patterns), will receive a cancel notification. The default behaviour is 'cancel'.

The alternative is 'forget'. The children that have not replied do not receive any notification, instead their root expression (their whole branch thus) are tagged as forgotten, they continue their execution, but their branch will not join the flow. It resumes without it.

Don't forget that those sync attributes are also available for the 'concurrent-iterator' expression.

Some 'concurrence' examples


    <concurrence>
        <participant ref="supervisor1" />
        <participant ref="supervisor2" />
    <concurrence>

Supervisor 1 and 2 will each receive a workitem 'simultaneously'.


    <concurrence
        count="2"
    >
        <participant ref="reviewer1" />
        <participant ref="reviewer2" />
        <participant ref="reviewer3" />
    <concurrence>

As soon as two reviewers proceeded their workitem (handled back the workitem to the engine through their user interface), the flow will continue. The last reviewer branch will get cancelled (his workitem will get removed from his worklist if he wasn't working on it).


    <sequence>
        <set field="approved?" value="false" />
        <concurrence>
            <participant ref="head1" />
            <participant ref="head2" />
        <concurrence>
        <if>
            <equals field-value="approved?" other-value="true" />
            <!-- then -->
            <subprocess ref="build_new_building" />
        </if>
    </sequence>

An important synchronization aspect focuses on what happens to workitems payload. By default, the 'merge' behaviour is 'first', that means that first participant to reply within the concurrence will see its workitem payload 'win'. In our example, the first of the two heads to reply will see its value for the field 'approved?' be taken into account for the building decision just after the concurrence.


    <concurrence
        merge="last"
    >
        <participant ref="head1" />
        <participant ref="head2" />
    <concurrence>

The workitem of the last 'head' to reply will be used to resume the flow after the concurrence (after both heads replied).


    <concurrence
        merge="last"
        merge-type="mix"
    >
        <participant ref="head1" />
        <participant ref="head2" />
    <concurrence>

The workitem that will resume the flow after the concurrence will result from a 'mix' of the workitem as replied by the heads. The mix will be performed field per field, with the 'last' reply always winning.


    <sequence>
        <concurrence
            count="2"
            remaining="forget"
        >
            <participant ref="reviewer1" />
            <participant ref="reviewer2" />
            <participant ref="reviewer3" />
        <concurrence>

        <participant ref="editor" />
    </sequence>

In this small flow snippet, the concurrence will resume to participant 'editor' once two of the reviewers will have replied. The last reviewer to reply will get forgotten (he will be able to manipulate his workitem, but upon receiving it back, the engine will simply discard it).