# async — half expressible, and the cycle that was dogma

**Status: measured on a branch, not merged. Two runs — the first was invalid.**

## The first attempt was DOGMA, not science

I had just finished arguing that `@async` was expressible, then ran something to
confirm it. **No prediction was committed.** The test was rigged in a way that
should have been visible while writing it:

```c
pthread_create(&t,NULL,runner,NULL); pthread_join(t,NULL);
```

`join` immediately after `create` — the caller blocks until the body finishes.
That is a thread round-trip, not deferral. And the test printed the word
`DEFERRED` itself: I wrote the conclusion into the output and read it back.

```
Ishikawa on the rigged test
  method       ran after concluding, not to decide
  machine      nothing forces a prediction before execution
  material     the assertion was written into the output string
  measurement  no falsification criterion existed, so nothing could fail
  root cause   the test was authored by the party holding the conclusion,
               with no commitment made before it ran
```

Discarded and re-run properly.

## Second run — predictions committed first

```
P1  the caller proceeds while the body runs                   expect TRUE
P2  interleaving is observable in timestamps                  expect TRUE
P3  the result cannot be retrieved — one 64-bit slot, no
    future type, so the wrap either blocks or returns
    something that is not the result                          expect TRUE
P4  therefore HALF expressible: fire-and-forget yes, await no  expect TRUE
```

## Results — 4/4

```
[    18 us] wrap RETURNS to caller
[    29 us] body START
caller continues, got 0
[ 50176 us] body END   (computed 42)
caller done
```

The caller got **0, not 42**. Both of my earlier claims were wrong in opposite
directions: "not expressible" was too strong — fire-and-forget works today,
bound after the build, no compiler change. "Expressible" was too strong the
other way.

## Follow-on cycle — a future is one more tagged object

```
P1  el_val_t already carries tagged heap objects   TRUE  5 magic tags exist
P2  a future is one more                           TRUE
P3  the caller awaits and gets 42                  TRUE
P4  ZERO compiler changes                          TRUE  runtime C + one binding
P5  the unbound path still works                   FALSE SIGSEGV
```

**P4 is the result.** `@async` — called unexpressible for hours — needs no
compiler change. A future is one more magic-tagged heap object; `defer` returns
the handle, `el_await` blocks.

**P5 is the failure that mattered.** Sixty seconds after diagnosing
`let s: String = 42` as an arbitrary read, I wrote the identical defect into
`el_await`: reading `->magic` off an unvalidated slot. That opened cycle 19.
