Workflow Patterns evaluation
View as MarkdownThe Workflow Patterns are a catalog of recurring behaviors in business processes. The Workflow Patterns Initiative defined them, led by Wil van der Aalst and Arthur ter Hofstede. Researchers use them to compare workflow languages and engines, such as BPMN, BPEL, and YAWL. Each pattern has an ID, a name, and a precise definition. See the control-flow patterns for the definitions.
This page rates Dex against the 43 control-flow patterns, WCP-1 to WCP-43. The data, resource, and exception-handling patterns are not evaluated here.
Rating scheme
The ratings use the scheme of the published evaluations.
- + means direct support. One Dex construct meets every part of the definition. The application keeps no extra state for it.
- +/- means indirect support. Dex implements the pattern by composing several constructs, such as Channel signals, an Attribute counter, a lock, an RPC guard, or a loop that waits again. In a few rows, one part of the definition holds in a limited form, and the row says which. The published evaluations call this rating partial support.
- - means no support.
A Dex construct is part of the programming model. It is a Primitive, a Step decision, a wait, or an SDK API that all five SDKs provide and document as the standard way to do the job. The Join is such an API.
How Dex joins parallel branches
The Join synchronizes parallel branches inside one Flow. It is built on a ChannelMap, and all five SDKs provide it.
- The split Step forks a round. Fork starts every branch and one join Step in the same decision. It gives them all the same round token.
- The token holds the round key and the branch count. The round key comes from the Step execution that forked the round. So the application keeps no counter and no key.
- Each branch reports one result when it ends.
- The join Step waits for every branch, for the first one, or for n of them. Its wait is an ordinary condition, so it combines with a Timer.
- Results of one round never count toward another round.
- When the join fires before every branch reports, the next round's split Step first waits for the rest of the previous round. This is the reset. It needs no extra Step.
When the work runs in child Flows, a SubFlow wait with AllOf is the native join.
Ten patterns stay +/-, for these reasons:
- WCP-15 and WCP-36 add instances while others run. A Join needs the branch count when it forks, so neither example uses one. WCP-15 keeps a start counter and an intake flag under a lock, and its join waits again after each result. WCP-36 keeps a flag that closes admission, an AttributeMap of started bids, and a lock, and its join waits again after each result until its condition holds.
- WCP-33 queues the triggers of each incoming branch separately and keeps extra triggers for later firings. A Join counts results per round. It does not queue them per branch.
- WCP-38 must decide from the process structure that no more threads can reach the merge. That is a reachability question, not a count.
- WCP-17 and WCP-40 rest on an Attribute lock, which covers one Execute only.
- WCP-18, WCP-20, WCP-23, and WCP-39 are not joins, so the Join does not change them. Their rows give the reasons for their ratings.
Limits of the Join
The Join counts the results that branches report. It does not watch the branch Steps. These limits follow.
- Each branch must report exactly once per round, on every exit path. That includes the Step that a failure route goes to. If a branch never reports, a join that waits for every branch waits forever. A branch that reports twice counts twice.
- A branch that is canceled from outside, for example by an RPC, never reports. A join that waits for every branch then waits forever. Cancel the join together with the branches, or wait for the join and a Timer with AnyOf.
- Canceling a branch from outside does not change the ratings. In Petri nets and YAWL, canceling a task inside a synchronized region also deadlocks the AND-join, unless the cancel region includes the join.
- A cancelling join's round is never reset. Its canceled branches never report, so a wait for the rest would never finish. In a loop, the next round forks a new round directly. Its round key keeps it apart from the old round.
- A join Step can be canceled after its wait consumed results but before its Execute result is saved. Whether those results return to the round depends on the server version. See canceled Steps and consumed messages. When they are lost, a new join for the same round never fires.
How the ratings were checked
Every pattern has a runnable example in TypeScript, Python, Go, Java, and Rust. Each example has an integration test against a real Dex Server. The test asserts the behavior that defines the pattern. For example, the Deferred Choice test sends two competing responses and checks that exactly one branch runs.
Summary
- 33 patterns have direct support (+).
- 10 patterns have indirect support (+/-).
- No pattern is unsupported (-).
Comparison with a published evaluation
A published evaluation rated six workflow and BPM systems against the same 43 control-flow patterns. Its authors are Wohed, Russell, ter Hofstede, Andersson, and van der Aalst. It appeared in Information and Software Technology 51(8), 2009 (PDF). In its Table 20, each system directly supports 11 to 20 patterns and partly supports up to 6. Each system leaves 19 to 32 patterns unsupported. Dex leaves none unsupported.
Results
The table names the Dex construct behind each rating.
| Pattern | Rating | Dex construct | Examples |
|---|---|---|---|
| WCP-1 Sequence | + | GoTo | TS · Python · Go · Java · Rust |
| WCP-2 Parallel Split | + | GoToMany | TS · Python · Go · Java · Rust |
| WCP-3 Synchronization | + | Join: Fork starts the branches and the join Step; the join waits for ForAllBranches | TS · Python · Go · Java · Rust |
| WCP-4 Exclusive Choice | + | Execute returns GoTo to one Step | TS · Python · Go · Java · Rust |
| WCP-5 Simple Merge | + | Several Steps GoTo the same Step type | TS · Python · Go · Java · Rust |
| WCP-6 Multi-Choice | + | GoToMany with a chosen subset of Steps | TS · Python · Go · Java · Rust |
| WCP-7 Structured Synchronizing Merge | + | Join: the choosing Step forks only the selected branches; the join waits for ForAllBranches | TS · Python · Go · Java · Rust |
| WCP-8 Multi-Merge | + | Each branch GoTo the same Step type; each arrival runs it again | TS · Python · Go · Java · Rust |
| WCP-9 Structured Discriminator | + | Join with ForBranches of 1; the next round's split Step resets it with ForRemainingBranches | TS · Python · Go · Java · Rust |
| WCP-10 Arbitrary Cycles | + | GoTo any Step; the cycle has two entries and two exits | TS · Python · Go · Java · Rust |
| WCP-11 Implicit Termination | + | GracefulComplete closes the Flow when no Step is active | TS · Python · Go · Java · Rust |
| WCP-12 Multiple Instances without Synchronization | + | GoToMany of one Step type; nothing waits for them | TS · Python · Go · Java · Rust |
| WCP-13 Multiple Instances with a priori Design-Time Knowledge | + | Join forks a fixed number of instances; the join waits for ForAllBranches | TS · Python · Go · Java · Rust |
| WCP-14 Multiple Instances with a priori Run-Time Knowledge | + | Join forks one instance per input item; the join waits for ForAllBranches | TS · Python · Go · Java · Rust |
| WCP-15 Multiple Instances without a priori Run-Time Knowledge | +/- | An RPC adds instances; an Attribute counts them; the join re-waits | TS · Python · Go · Java · Rust |
| WCP-16 Deferred Choice | + | AnyOf over Channels; only the chosen message is consumed, so one branch runs | TS · Python · Go · Java · Rust |
| WCP-17 Interleaved Parallel Routing | +/- | A shared Attribute lock on Execute; it covers one Execute only | TS · Python · Go · Java · Rust |
| WCP-18 Milestone | +/- | An Attribute marks the state; an RPC starts the task only while it holds | TS · Python · Go · Java · Rust |
| WCP-19 Cancel Task | + | RPC result cancels a Step type and starts the next Step | TS · Python · Go · Java · Rust |
| WCP-20 Cancel Case | +/- | The Step that starts a SubFlow also waits for a cancel Channel, stops the child, and returns ForceFail | TS · Python · Go · Java · Rust |
| WCP-21 Structured Loop | + | A pre-test Step GoTo the loop body or the exit | TS · Python · Go · Java · Rust |
| WCP-22 Recursion | + | A Step waits for a SubFlow of its own Flow type | TS · Python · Go · Java · Rust |
| WCP-23 Transient Trigger | +/- | An RPC guard under a shared Attribute lock accepts one trigger while a Step waits | TS · Python · Go · Java · Rust |
| WCP-24 Persistent Trigger | + | Channel messages stay queued until a Step consumes them | TS · Python · Go · Java · Rust |
| WCP-25 Cancel Region | + | RPC result cancels several Step types at once | TS · Python · Go · Java · Rust |
| WCP-26 Cancel Multiple Instance Activity | + | RPC result cancels the instance Step type; finished instances keep their results | TS · Python · Go · Java · Rust |
| WCP-27 Complete Multiple Instance Activity | + | RPC result cancels the instances and the join, then starts the next Step | TS · Python · Go · Java · Rust |
| WCP-28 Blocking Discriminator | + | Join with ForBranches of 1; the next round starts only after ForRemainingBranches resets the join | TS · Python · Go · Java · Rust |
| WCP-29 Cancelling Discriminator | + | Join with ForBranches of 1; the join's decision cancels its sibling branches | TS · Python · Go · Java · Rust |
| WCP-30 Structured Partial Join | + | Join with ForBranches of n; the next round's split Step resets it with ForRemainingBranches | TS · Python · Go · Java · Rust |
| WCP-31 Blocking Partial Join | + | Join with ForBranches of n; the next round starts only after ForRemainingBranches resets the join | TS · Python · Go · Java · Rust |
| WCP-32 Cancelling Partial Join | + | Join with ForBranches of n; the join's decision cancels its sibling branches | TS · Python · Go · Java · Rust |
| WCP-33 Generalised AND-Join | +/- | AllOf over Channels; extra signals wait for the next firing | TS · Python · Go · Java · Rust |
| WCP-34 Static Partial Join for Multiple Instances | + | Join over m instances with ForBranches of n; the next round's split Step resets it with ForRemainingBranches | TS · Python · Go · Java · Rust |
| WCP-35 Cancelling Partial Join for Multiple Instances | + | Join over m instances with ForBranches of n; the join's decision cancels its sibling instances | TS · Python · Go · Java · Rust |
| WCP-36 Dynamic Partial Join for Multiple Instances | +/- | An RPC adds instances; the join re-waits and tests its condition | TS · Python · Go · Java · Rust |
| WCP-37 Local Synchronizing Merge | + | Join: the diverging Step forks the active branches; the join waits for ForAllBranches | TS · Python · Go · Java · Rust |
| WCP-38 General Synchronizing Merge | +/- | An Attribute counts the threads that can still reach the merge; reaching 0 publishes a sentinel | TS · Python · Go · Java · Rust |
| WCP-39 Critical Section | +/- | A one-message Channel acts as a token | TS · Python · Go · Java · Rust |
| WCP-40 Interleaved Routing | +/- | A shared Attribute lock on Execute, then ForN; the lock covers one Execute only | TS · Python · Go · Java · Rust |
| WCP-41 Thread Merge | + | Join over the threads of one Step type; the merge waits for ForAllBranches | TS · Python · Go · Java · Rust |
| WCP-42 Thread Split | + | GoToMany of one Step type | TS · Python · Go · Java · Rust |
| WCP-43 Explicit Termination | + | ForceComplete closes the Flow and cancels other Steps | TS · Python · Go · Java · Rust |
What the ratings rest on
The ratings depend on these Dex behaviors.
- GracefulComplete closes the Flow only when no Step is active.
- DeadEnd ends one branch and never closes the Flow.
- ForceComplete closes the Flow at once and cancels the other Steps.
- AnyOf consumes messages only from the condition it selects.
- A Channel message stays queued until a Step consumes it.
- A decision or an RPC cancels Steps by Step type. Dex does not stop a canceled method's code. The method keeps running until it checks for cancellation and returns.
- Only one Step method or RPC holds an Attribute lock at a time.
- A SubFlow keeps running when its parent closes.
- A Join keys each round by the Step execution that forked it. Results of one round never satisfy the join of another round.
- A Step method publishes only when it succeeds. So a retried branch reports its result once.
- A decision can limit its cancellation to sibling Steps. Siblings are the Steps that one decision started together. When an RPC forked a round with an explicit round key, the branches are siblings of every Step that this RPC name ever started. Cancel such branches explicitly by Step type.
See Step, Channel, and SubFlow for these concepts.
Pattern details
WCP-1 Sequence
What it is. A task starts only after the task before it finishes. Both tasks belong to the same process. See the official definition.
How Dex implements it. Each Step's Execute returns GoTo for the next Step. The example runs ReceiveOrder, CheckInventory, and ShipOrder in order.
Rating: +. GoTo is the sequence construct.
Examples: TS · Python · Go · Java · Rust
WCP-2 Parallel Split
What it is. One branch splits into two or more branches. The branches run at the same time. See the official definition.
How Dex implements it. A Step returns GoToMany with one Step movement per branch. The example splits an order into packing and payment.
Rating: +. GoToMany is the split construct.
Examples: TS · Python · Go · Java · Rust
WCP-3 Synchronization
What it is. Two or more branches converge into one. The next branch starts only after every incoming branch has finished. See the official definition.
How Dex implements it. The split Step calls Fork on a Join. Fork returns the branches and the join Step for one GoToMany, and gives each of them the same round token. Each branch ends with EndBranch, which reports its result. The join Step waits for ForAllBranches.
Rating: +. The Join is the join construct. Fork counts the branches and keys the round, so the application keeps no count.
Examples: TS · Python · Go · Java · Rust
WCP-4 Exclusive Choice
What it is. One branch splits into several. Control passes to exactly one of them. A condition picks which one. See the official definition.
How Dex implements it. Execute evaluates the condition and returns GoTo for one Step. The example routes a claim to manual review or automatic approval by amount.
Rating: +. A decision with one GoTo is the choice construct.
Examples: TS · Python · Go · Java · Rust
WCP-5 Simple Merge
What it is. Two or more branches converge into one. Each arrival passes control on. The pattern assumes only one incoming branch is active at a time. See the official definition.
How Dex implements it. Each branch returns GoTo for the same Step type. The example's two review branches both go to CloseClaim.
Rating: +. A shared GoTo target is the merge.
Examples: TS · Python · Go · Java · Rust
WCP-6 Multi-Choice
What it is. One branch splits into several. Control passes to one or more of them. A condition picks the set. See the official definition.
How Dex implements it. Execute builds the list of selected Steps and returns GoToMany for them. The example picks email, SMS, or push notifications from the input.
Rating: +. GoToMany with a computed list is the multi-choice construct.
Examples: TS · Python · Go · Java · Rust
WCP-7 Structured Synchronizing Merge
What it is. Branches started by an earlier Multi-Choice converge into one. The merge waits for every branch that was started, and only those. It belongs to one Multi-Choice in a structured model. See the official definition.
How Dex implements it. The choosing Step passes only the selected branches to Fork. Each branch ends with EndBranch. The join waits for ForAllBranches, so it waits for exactly the branches that started.
Rating: +. The branch count comes from Fork. The application never passes it to the join.
Examples: TS · Python · Go · Java · Rust
WCP-8 Multi-Merge
What it is. Several branches converge into one. Each arrival passes control on separately. The next task runs once per arrival. See the official definition.
How Dex implements it. Each branch returns GoTo for the same Step type. Dex starts a new execution of that Step for each arrival.
Rating: +. Each GoTo creates its own Step execution.
Examples: TS · Python · Go · Java · Rust
WCP-9 Structured Discriminator
What it is. After a parallel split, the first branch to arrive passes control on. Later arrivals do not. The construct resets once every branch has arrived. See the official definition.
How Dex implements it. The split Step forks each round of a Join. The join waits for ForBranches of 1 and passes control on. It also starts the next round's split Step with the round token. That Step's WaitFor waits for ForRemainingBranches, which consumes the other results of the round. That is the reset. The Step then forks the next round. The example records the ignored results in an Attribute.
Rating: +. The Join keys each round by the Step execution that forked it, so a late result never passes control on. The reset needs no extra Step and no round counter.
Examples: TS · Python · Go · Java · Rust
WCP-10 Arbitrary Cycles
What it is. A process can contain a loop with more than one entry point or exit point. See the official definition.
How Dex implements it. Steps GoTo each other freely, including Steps that already ran. The example's draft and edit loop has two entry points and two exit points.
Rating: +. GoTo can target any Step.
Examples: TS · Python · Go · Java · Rust
WCP-11 Implicit Termination
What it is. A process ends when no work remains, now or later. No dedicated end node is needed. See the official definition.
How Dex implements it. Each branch that finishes returns GracefulComplete. The Flow closes only when no Step is active.
Rating: +. GracefulComplete waits for the last active Step.
Examples: TS · Python · Go · Java · Rust
WCP-12 Multiple Instances without Synchronization
What it is. A task runs as several independent instances in one process instance. Nothing waits for them to finish. See the official definition.
How Dex implements it. A Step returns GoToMany with one execution of the same Step type per instance. The Flow continues on another branch without a join.
Rating: +. GoToMany starts the instances, and no Step waits for them.
Examples: TS · Python · Go · Java · Rust
WCP-13 Multiple Instances with a priori Design-Time Knowledge
What it is. The number of instances is fixed in the model. The instances run at the same time. The process continues after all of them finish. See the official definition.
How Dex implements it. A Step forks a Join with a fixed list of instances. Each instance ends with EndBranch. The join waits for ForAllBranches.
Rating: +. Each instance is one branch of the Join, and Fork counts them.
Examples: TS · Python · Go · Java · Rust
WCP-14 Multiple Instances with a priori Run-Time Knowledge
What it is. The number of instances is known at run time, before they start. The instances run at the same time. The process continues after all of them finish. See the official definition.
How Dex implements it. A Step builds one instance for each item in its input and forks a Join with that list. The join waits for ForAllBranches.
Rating: +. The number of instances is the length of the list that Fork receives. The application keeps no count.
Examples: TS · Python · Go · Java · Rust
WCP-15 Multiple Instances without a priori Run-Time Knowledge
What it is. The number of instances is not known until the last one finishes. New instances can start while others run. The process continues after all of them finish. See the official definition.
How Dex implements it. An RPC starts each new instance through its next Steps and counts it in an Attribute. Another RPC prevents further instances from starting. The join waits for one result at a time. It loops until no new instances can start and every instance has reported.
Rating: +/-. The join composes an RPC, an Attribute counter, and a loop that waits again after each result.
Examples: TS · Python · Go · Java · Rust
WCP-16 Deferred Choice
What it is. The environment chooses between branches. Every branch stays possible until one of them starts. Then the others are withdrawn. See the official definition.
How Dex implements it. One Step waits with AnyOf over one Channel per branch. Dex consumes the message of the selected condition only. Execute goes to the branch that matches it.
Rating: +. AnyOf decides the race, and only one branch ever starts.
Design pattern: Deferred Choice.
Examples: TS · Python · Go · Java · Rust
WCP-17 Interleaved Parallel Routing
What it is. A set of tasks runs in any order that a partial order allows. Each task runs once. No two of them run at the same time. See the official definition.
How Dex implements it. Every task's Execute declares a lock on the same Attribute. GoTo encodes the partial order. A join waits for ForN signals from the tasks.
Rating: +/-. The routing composes a lock with Channel signals. The lock covers one Execute only. It does not cover a WaitFor or a task that spans several Steps.
Examples: TS · Python · Go · Java · Rust
WCP-18 Milestone
What it is. A task can start only while the process is at a given state. Once the process moves past that state, the task can no longer start. See the official definition.
How Dex implements it. An Attribute marks that the change window is open. An RPC starts the task through its next Steps only while the Attribute is set. A later Step clears it.
Rating: +/-. A Step cannot test another branch's state, so an Attribute and an RPC guard stand in for it.
Examples: TS · Python · Go · Java · Rust
WCP-19 Cancel Task
What it is. An enabled task is withdrawn before it starts. A running task is stopped where possible. See the official definition.
How Dex implements it. An RPC result cancels the waiting Step by Step type and starts another Step. Dex applies both changes together.
Rating: +. Canceling Steps is a built-in RPC result.
Examples: TS · Python · Go · Java · Rust
WCP-20 Cancel Case
What it is. A whole process instance is removed. This includes running tasks, future tasks, and "all sub-processes". It is recorded as unsuccessful. See the official definition.
How Dex implements it. The Step that starts a SubFlow waits with AnyOf for the child or a cancel Channel. On cancel, it reads the child's Flow ID with SubFlow.getFlowId, stops the child with Client StopFlow, and returns ForceFail. ForceFail cancels every other active Step.
Rating: +/-. Client StopFlow alone leaves a running SubFlow alive, so the example composes a cancel Channel, an explicit stop, and ForceFail.
Design pattern: Cancel with Child Flows.
Examples: TS · Python · Go · Java · Rust
WCP-21 Structured Loop
What it is. A task or sub-process repeats while a condition holds. The condition is tested before or after each pass. The loop has one entry and one exit. See the official definition.
How Dex implements it. A pre-test Step GoTo the loop body or the exit. The body GoTo the pre-test again.
Rating: +. GoTo forms the loop.
Examples: TS · Python · Go · Java · Rust
WCP-22 Recursion
What it is. A task can invoke itself, directly or through an ancestor. See the official definition.
How Dex implements it. A Step waits for a SubFlow of its own Flow type. The example computes a factorial this way.
Rating: +. A SubFlow can run the same Flow type as its parent.
Examples: TS · Python · Go · Java · Rust
WCP-23 Transient Trigger
What it is. A signal triggers a task only if the task is already waiting. A signal that arrives when no task waits is lost. See the official definition.
How Dex implements it. The Step's WaitFor sets an Attribute to record that it is waiting. An RPC declares a lock on that Attribute. It accepts a trigger only while the Attribute is set, and it clears the Attribute when it accepts one. Later triggers are dropped.
Rating: +/-. Channel messages persist, so the example composes an Attribute, a lock, and an RPC guard to drop triggers.
Examples: TS · Python · Go · Java · Rust
WCP-24 Persistent Trigger
What it is. A signal is kept until a task can act on it. See the official definition.
How Dex implements it. An RPC publishes the signal to a Channel. The message stays queued until a Step's WaitFor consumes it.
Rating: +. Channel messages persist by design.
Examples: TS · Python · Go · Java · Rust
WCP-25 Cancel Region
What it is. A set of tasks is withdrawn together. The tasks need not be connected. See the official definition.
How Dex implements it. An RPC result names several Step types to cancel and starts the next Step. Other Steps keep running.
Rating: +. One RPC result cancels any set of Step types.
Examples: TS · Python · Go · Java · Rust
WCP-26 Cancel Multiple Instance Activity
What it is. A multiple-instance task can be canceled at any time. Unfinished instances are withdrawn. Finished instances keep their results. See the official definition.
How Dex implements it. An RPC result cancels the instance Step type. Instances that already completed are unaffected.
Rating: +. Canceling by Step type covers every open instance.
Examples: TS · Python · Go · Java · Rust
WCP-27 Complete Multiple Instance Activity
What it is. A multiple-instance task can be forced to complete. Remaining instances are withdrawn, and the process continues. See the official definition.
How Dex implements it. An RPC result cancels the instances and their join, then starts the next Step with the results so far.
Rating: +. One RPC result both cancels the instances and starts the next Step.
Examples: TS · Python · Go · Java · Rust
WCP-28 Blocking Discriminator
What it is. The first branch to arrive passes control on. The definition says "Subsequent enablements of incoming branches are blocked until the Blocking Discriminator has reset." It resets once every active branch has arrived. See the official definition.
How Dex implements it. The split Step forks each round of a Join, and the join waits for ForBranches of 1. The next round's split Step waits for ForRemainingBranches of the previous round before it forks again. Until then, no branch of the next round exists, so a next-round signal stays queued and has no effect.
Rating: +. The reset blocks the next round by construction. It needs no extra Step and no round counter.
Examples: TS · Python · Go · Java · Rust
WCP-29 Cancelling Discriminator
What it is. The first branch to arrive passes control on. It also cancels the other branches. See the official definition.
How Dex implements it. The split Step forks the branches with a Join, and the join waits for ForBranches of 1. Its decision cancels its sibling Steps of the branch Step types. Fork starts the branches and the join in one decision, so the siblings are exactly this round's branches.
Rating: +. One join Step both fires and cancels. A canceled branch never reports a result.
Examples: TS · Python · Go · Java · Rust
WCP-30 Structured Partial Join
What it is. After a parallel split into m branches, control passes on once n of them arrive. Later arrivals do not pass control on. The join resets after every branch arrives. See the official definition.
How Dex implements it. The split Step forks each round of a Join with m branches. The join waits for ForBranches of n. The next round's split Step waits for ForRemainingBranches of the previous round, which consumes the other m - n results. Then it forks the next round. The example records the ignored results in an Attribute.
Rating: +. As in WCP-9, the Join keys each round, and the reset needs no extra Step and no round counter.
Examples: TS · Python · Go · Java · Rust
WCP-31 Blocking Partial Join
What it is. Control passes on once n of m branches arrive. Further enablements are blocked until the join resets. See the official definition.
How Dex implements it. This works like WCP-28 with ForBranches of n. The next round's branches start only after ForRemainingBranches resets the join, so next-round signals stay queued until then.
Rating: +. As in WCP-28, the reset blocks the next round by construction.
Examples: TS · Python · Go · Java · Rust
WCP-32 Cancelling Partial Join
What it is. Control passes on once n of m branches arrive. The remaining branches are canceled. See the official definition.
How Dex implements it. This works like WCP-29 with ForBranches of n. The join's decision cancels its sibling branches.
Rating: +. As in WCP-29, one join Step both fires and cancels.
Examples: TS · Python · Go · Java · Rust
WCP-33 Generalised AND-Join
What it is. Control passes on once every incoming branch has delivered a trigger. Extra triggers on a branch are kept for later firings. See the official definition.
How Dex implements it. The join waits with AllOf over one Channel per incoming branch. Extra messages stay queued, and the join loops to fire again.
Rating: +/-. The join composes Channel signals with an AllOf wait. The branches must publish the signals.
Examples: TS · Python · Go · Java · Rust
WCP-34 Static Partial Join for Multiple Instances
What it is. m instances run. The next task starts once n of them complete. The rest still finish, and the join resets after all complete. See the official definition.
How Dex implements it. In each round, the split Step forks a Join with m instances of one Step type. The join waits for ForBranches of n. The next round's split Step waits for ForRemainingBranches of the previous round, then forks the next round. The example records the ignored results in an Attribute.
Rating: +. As in WCP-30, with instances as the branches.
Examples: TS · Python · Go · Java · Rust
WCP-35 Cancelling Partial Join for Multiple Instances
What it is. m instances run. Once n of them complete, the next task starts and the rest are canceled. See the official definition.
How Dex implements it. The split Step forks m instances with a Join, and the join waits for ForBranches of n. Its decision cancels its sibling Steps of the instance Step type.
Rating: +. As in WCP-32, with instances as the branches.
Examples: TS · Python · Go · Java · Rust
WCP-36 Dynamic Partial Join for Multiple Instances
What it is. Instances can start while others run, until adding is disabled. A completion condition is checked after each instance finishes. When it holds, the next task starts. See the official definition.
How Dex implements it. An RPC starts a new instance unless an Attribute flag says new instances are disabled. The join waits for one result at a time and tests its condition. When the condition holds, the join sets the flag and continues.
Rating: +/-. The join composes an RPC, an Attribute flag, a shared lock, and a loop that waits again after each result.
Examples: TS · Python · Go · Java · Rust
WCP-37 Local Synchronizing Merge
What it is. Branches converge, and the merge waits for every active branch. It decides how many to wait for from information it has locally. See the official definition.
How Dex implements it. As in WCP-7, the diverging Step forks a Join with only the active branches. The join waits for ForAllBranches.
Rating: +. The local information is the branch count in the round token, which Fork creates.
Examples: TS · Python · Go · Java · Rust
WCP-38 General Synchronizing Merge
What it is. The merge waits for every active branch, or until "it is not possible that any branch that has not yet been enabled will be enabled at any future time". It works in any process, including unstructured ones with loops. See the official definition.
How Dex implements it. An Attribute counts the threads that can still reach the merge. Every Step that changes the count declares a lock on that Attribute. The Step that brings the count to 0 also publishes a "done" message to a Channel. The merge Step waits for that message.
Rating: +/-. The pattern expects the merge to decide reachability from the process structure. In Dex, application code decides it at each GoTo.
Examples: TS · Python · Go · Java · Rust
WCP-39 Critical Section
What it is. Several sub-processes are marked as critical sections. Only one of them runs at a time in a process instance. See the official definition.
How Dex implements it. A Channel holds a single token message. Each section's first Step waits for the token, and its last Step publishes it back.
Rating: +/-. An Attribute lock covers one Step method only, so a section of several Steps uses a one-message Channel as a token.
Design pattern: Critical Section.
Examples: TS · Python · Go · Java · Rust
WCP-40 Interleaved Routing
What it is. Each task in a set runs once, in any order. No two of them run at the same time. The next task starts after all of them finish. See the official definition.
How Dex implements it. Every task's Execute declares a lock on the same Attribute. A join waits for ForN signals from the tasks.
Rating: +/-. The routing composes a lock with Channel signals. As in WCP-17, the lock covers one Execute only.
Examples: TS · Python · Go · Java · Rust
WCP-41 Thread Merge
What it is. A given number of threads in one branch merge into a single thread. See the official definition.
How Dex implements it. A Step forks a Join with the given number of threads, all of one Step type. Each thread ends with EndBranch. The merge Step waits for ForAllBranches.
Rating: +. Each thread is one branch of the Join, and Fork counts them.
Examples: TS · Python · Go · Java · Rust
WCP-42 Thread Split
What it is. A branch starts a given number of threads. See the official definition.
How Dex implements it. A Step returns GoToMany with that many executions of one Step type.
Rating: +. GoToMany starts the threads.
Examples: TS · Python · Go · Java · Rust
WCP-43 Explicit Termination
What it is. A process ends when it reaches a given end node. Remaining work is canceled, and the process is recorded as successful. See the official definition.
How Dex implements it. A Step returns ForceComplete. Dex closes the Flow at once and cancels every other active Step.
Rating: +. ForceComplete is the explicit end.