Skip to content

🐛 Add support for operation stacking#89

Merged
kammce merged 1 commit intomainfrom
add-async-stacking
Apr 1, 2026
Merged

🐛 Add support for operation stacking#89
kammce merged 1 commit intomainfrom
add-async-stacking

Conversation

@kammce
Copy link
Copy Markdown
Member

@kammce kammce commented Apr 1, 2026

Operation stacking is when you load a context with multiple coroutines. Before, this resulted in a memory leak, due to the active_handle being overwritten and lost. This also results in lifetime violation if the context stack, prior to the new coroutine being loaded, had objects with lifetimes that need to be destroyed. This change enables operation stacking so it is no longer erroneous behavior. Now the last coroutine loaded is the first one to be executed (LIFO) and up the chain until the first loaded coroutine is reached.

Summary

  • Enables multiple coroutines to be loaded onto the same context in LIFO order — the last routine pushed runs first, allowing natural "stacking" of async work on a single context
  • Bans co-awaiting an l-value future (deleted operator co_await() &) to prevent accidentally awaiting a future from a different context; co-awaiting a future whose context was allocated inside a coroutine frame is now a contract violation (contract_assert) or std::terminate
  • Removes [[clang::lifetimebound]] from awaiter constructor
  • Simplify await_suspend to just return the stored handle and drop the explicit continuation chain. Continuation is now captured at get_return_object() time via m_context->active_handle() to enable operation stacking.
  • Exceptions now keep a void* for the offending future or context.

Test plan

  • tests/async_stacking.test.cpp — LIFO ordering with two and three routines on the same context
  • tests/context_swapping.test.cpp — verifies that co-awaiting a future from a mismatched nested context triggers std::terminate (or contract violation when contracts are enabled)
  • Existing tests/basics.test.cpp continues to pass with the updated await_ready logic
  • All CI checks pass
  • New/updated tests cover the changes
  • Tested locally with conan create .

Operation stacking is when you load a context with multiple coroutines.
Before, this resulted in a memory leak, due to the active_handle being
overwritten and lost. This also results in lifetime violation if the
context stack, prior to the new coroutine being loaded, had objects
with lifetimes that need to be destroyed. This change enables operation
stacking so it is no longer erroneous behavior. Now the last coroutine
loaded is the first one to be executed (LIFO) and up the chain until
the first loaded coroutine is reached.

\## Summary

- Enables multiple coroutines to be loaded onto the same `context` in
  LIFO order — the last routine pushed runs first, allowing natural
  "stacking" of async work on a single context
- Bans co-awaiting an l-value `future` (deleted `operator co_await() &`)
  to prevent accidentally awaiting a future from a different context;
  co-awaiting a future whose context was allocated inside a coroutine
  frame is now a contract violation (`contract_assert`) or
  `std::terminate`
- Removes `[[clang::lifetimebound]]` from `awaiter` constructor
- Simplify `await_suspend` to just return the stored handle and drop the
  explicit continuation chain. Continuation is now captured at
  `get_return_object()` time via `m_context->active_handle()` to enable
  operation stacking.

\## Test plan

- [x] `tests/async_stacking.test.cpp` — LIFO ordering with two and three routines on the same context
- [x] `tests/context_swapping.test.cpp` — verifies that co-awaiting a future from a mismatched nested context triggers `std::terminate` (or contract violation when contracts are enabled)
- [x] Existing `tests/basics.test.cpp` continues to pass with the updated `await_ready` logic
@kammce kammce merged commit 37ac9ca into main Apr 1, 2026
6 of 8 checks passed
@kammce kammce deleted the add-async-stacking branch April 1, 2026 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant