# Futures and Async Execution

## Overview

A Choreo future names the completion of an operation. Futures are local
producer-side handles. Events publish completion to another asynchronous path.

There are two future categories:

- A data future is produced by DMA or TMA. It also refers to the destination
  data through `.data` and `.span`.
- An operation future is produced by an asynchronous operation that updates an
  existing object. Asynchronous WGMMA is the first operation-future producer.

```choreo
load = tma.load.async input => tile;
qk = mma.row.row.async scores, q_tile, k_tile;
```

`load` is a data future. `qk` is an operation future and does not own a data
buffer; the updated accumulator is `scores`.

## Waiting Locally

`wait` observes completion on the current control path:

```choreo
wait load;
wait qk;
wait lhs_load, rhs_load;
```

For a data future, the destination can be read after the wait. For an MMA
operation future, the accumulator result can be consumed after the wait. The
compiler derives the required WGMMA wait depth from the dependency graph; there
is no source-level wait-depth control.

Each dynamic future instance has one completion path. Waiting consumes that
producer-local instance; later cross-path notification is an ordinary direct
event trigger:

```choreo
qk = mma.row.row.async scores, q_tile, k_tile;
wait qk;
// Local work that consumes scores.
trigger qk_done;
```

Waiting or binding the same dynamic future instance more than once is an error.
A data-future symbol may still appear at multiple static wait sites in a
swap/rotation pipeline, where each execution observes a different dynamic
instance. If several consumers need one completion, publish one event and let
them wait on that event.

## Publishing Completion to an Event

`trigger event after future` binds an event to an operation's native completion
mechanism:

```choreo
trigger event_name after future_name;
```

One event can join several operations:

```choreo
lhs_load = tma.load.async lhs => lhs_s;
rhs_load = tma.load.async rhs => rhs_s;
trigger operands_full after lhs_load, rhs_load;
```

This form is non-blocking and never implies or inserts `wait future`. It is
valid only when the backend can hand the event directly to the operation. The
CUDA backend currently supports async global-to-shared TMA futures, whose
transaction byte counts are joined on the event's mbarrier.

WGMMA has no native completion-event operand. Make its completion point
explicit instead:

```choreo
qk = mma.row.row.async scores, q_tile, k_tile;
wait qk;
trigger k_empty;
```

Likewise, `wait qk; trigger k_empty after qk;` is invalid: after an explicit
wait there is no future left to bind, and the direct trigger states the order
without a redundant dependency.

Use an event for cross-path communication:

```choreo
parallel g by 2 : group-4 {
  inthreads.async (g == 0) {
    load = tma.load.async input => tile;
    trigger full after load;
  }
  inthreads.async (g == 1) {
    wait full;
    // Consume tile.
  }
}
```

## Cyclic Event Generations

A future has no stage. A cyclic event array derives its slot and generation
from the logical pipeline order:

```choreo
slot = order % 2;
wait empty.at(order);
load = tma.load.async input.at(order) => tile[slot];
trigger full.at(order) after load;
```

For `event[N]`, `.at(order)` selects slot `order % N` and generation
`order / N`. Ordinary data arrays still use an explicit physical slot. This
prevents a reused event slot from being confused with an older logical
iteration without introducing a separate stage type.

An event is pending by default. Use `= ready` when generation zero must be
available immediately:

```choreo
shared event full[2], empty[2] = ready;
```

See the included [Events snapshot](events.txt) for event generations and fanout.

## Data Future Members

Only data futures expose buffer members:

| Member | Description |
|--------|-------------|
| `.data` | Destination data reference |
| `.span` | Destination shape |

These members are available immediately for a synchronous copy and after
completion for an asynchronous copy. Operation futures do not expose either
member.

## Placeholder Data Futures

`dma.any` creates a placeholder data future for legacy buffer-rotation
patterns:

```choreo
next = dma.any;
foreach k in tiles {
  next = dma.copy.async input.chunkat(k) => shared;
  wait current;
  swap(current, next);
}
```

Placeholder data futures do not replace operation futures or events.

## Operation-to-Operation DMA Chains

The DMA `after` clause orders one DMA operation after another without
publishing an event:

```choreo
store = dma.copy.async result => output after load;
```

This is distinct from `trigger event after future`: the former creates an
operation-to-operation edge, while the latter binds native completion to an
event without waiting.

## Async Execution Levels

Choreo composes three kinds of asynchronous execution:

1. An async operation produces a future.
2. `inthreads.async` lets predicated thread groups execute independently.
3. `parallel.async` launches independent kernel work on a stream.

Futures express operation completion. Events express communication between
asynchronous paths. `event.at(order)` expresses cyclic event generations. No
`pipeline` keyword is required.
