# What happens to work a run stops waiting for

> A run that is waiting on child runs — one it awaits, several joined, tasks that wait — and is ended from somewhere else (an event sends it to a terminal, somebody stops it) calls those children off: each is stopped, at its own `terminal cancel`, and so is everything it was waiting for in turn. Nothing is left waiting for an answer nobody will read.

<!-- id: workflow-called-off · area: workflow · stability: stable · html: https://osysharp.com/reference/workflow/called-off/ -->

## Summary        {#summary}
A parent parked at `await Workflow.Run("pick", pick)` is waiting for that pick. If the order is withdrawn meanwhile —
an event arm sends the parent to a terminal — the body that was waiting is gone, and nobody will ever read the pick's
outcome. So the pick is **called off**: its run is stopped, and if the pick was itself waiting on something, that is
stopped too.

You write nothing for this. It is what ending a run means.

## Signature      {#signature}
```osy syntax
state Placed {
  subscribe Withdraw();
  enter { await Workflow.Run("pick", pick); goto Shipped; }   // parked here…
  on Withdraw { goto Withdrawn; }                              // …when this ends the run: the pick is stopped
}
```

## Description    {#description}

### Which runs are called off?   {#which}
The ones the run was **still waiting for** when the code waiting for them ended:

| the run was… | and then… | called off |
|---|---|---|
| parked at `await Workflow.Run(…)` | an event arm ended it, or somebody stopped it | the awaited child |
| parked at a join of legs ([Parallel legs (start several, then wait for them)](https://osysharp.com/reference/workflow/parallel-legs/)) | the same | every leg still running |
| parked on tasks that wait ([Tasks that wait (Task.WhenAll across days)](https://osysharp.com/reference/workflow/tasks-that-wait/)) | the same | the child each task waits on |
| past a `Task.WhenAny` | its body finished | the child the losing task still waits on |

### Which are not?   {#which-not}
Anything the run had stopped waiting for of its own accord:

- a child started **without** `await` — fire-and-forget is a run of its own ([Workflow.Run (start a workflow)](https://osysharp.com/reference/workflow/run/));
- a leg left running on purpose with `losers: Losers.Continue`;
- every child of a run whose own body carried it to its end — that body consumed its waits.

### Where does a called-off run end?   {#where}
At its workflow's own `terminal cancel` state, when it declares one: the state it is in runs its `exit { }`, its
slots and clocks retire, and its tracked field takes the terminal's value — so the row says what happened to it. A
workflow that declares several cancel terminals ends at the one named `Cancelled`; one that declares none is ended
without a transition, and its tracked field stays where it was.

Stopping a child ends **its** waits by the same rule, so a tree is called off level by level, however deep.

### Does compensation run?   {#compensation}
No. A called-off run is stopped where it stands, as the run that was waiting for it was. If withdrawing has to undo
something, say so where the withdrawal is written — in the arm that handles it — or keep the work inside a
[saga](https://osysharp.com/reference/workflow/beginsaga/) whose own `catch` routes the cancellation.

## Examples       {#examples}
```osy title="withdrawing the order stops the pick it was waiting for" run app=workflow-called-off
using Osysharp.Workflow;

enum OrderStatus { Placed, Shipped, Withdrawn }
enum PickStatus  { Picking, Picked, CalledOff }

entity Order {
  [Required, MaxLength(40)] string Reference;
  OrderStatus Status = OrderStatus.Placed;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

entity Pick {
  [Required] Order Order;
  PickStatus Status = PickStatus.Picking;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

workflow PickFlow {
  Tracks = Pick.Status; Autostart = false; Initial = Picking;
  event Report();
  state Picking { subscribe Report(); on Report { goto Picked; } }
  terminal success Picked    { }
  terminal cancel  CalledOff { Message = "nobody is waiting for this pick"; }
}

workflow OrderFlow {
  Tracks = Order.Status; Autostart = false; Initial = Placed;
  event Withdraw();
  state Placed {
    subscribe Withdraw();
    enter {
      var pick = Workflow.Once("make-pick", () => new Pick { Order = this.Item });
      await Workflow.Run("pick", pick);
      goto Shipped;
    }
    on Withdraw { goto Withdrawn; }
  }
  terminal success Shipped   { }
  terminal cancel  Withdrawn { Message = "the order was withdrawn"; }
}

[Test]
void withdrawing_the_order_stops_the_pick_it_was_waiting_for() {
  var order = new Order { Reference = "SO-1" };
  Workflow.Run(order);
  Workflow.Settle(order);
  var pick = Pick.Single(p => p.Order == order);
  Assert.Equal(PickStatus.Picking, pick.Status);        // the order is parked, waiting for it

  OrderFlow.RaiseWithdraw(order);
  Workflow.Settle(order);
  Assert.Equal(OrderStatus.Withdrawn, order.Status);
  Workflow.Settle(pick);
  Assert.Equal(PickStatus.CalledOff, pick.Status);      // …and so the pick is called off, at its own terminal
}
```

## See also       {#see-also}
- [Workflow.Run (start a workflow)](https://osysharp.com/reference/workflow/run/) — awaiting a child run
- [Parallel legs (start several, then wait for them)](https://osysharp.com/reference/workflow/parallel-legs/) — joins, and what happens to a leg that lost
- [Tasks that wait (Task.WhenAll across days)](https://osysharp.com/reference/workflow/tasks-that-wait/) — tasks that wait on child runs
- [Transitions — where this item may go next](https://osysharp.com/reference/workflow/transitions/) — how an event arm moves a run
