Commit 0646567
Shrink the inline storage in connect_awaitable (#1974)
* Shrink the inline storage in connect_awaitable
This diff adapts an idea originally due to @lewissbaker, originally
shared at https://godbolt.org/z/zGG9fsPrz.
Changes to Lewis's original include:
* never invoke `coro.destroy()` since it's a no-op anyway
* hard-code the integration with `connect_awaitable` since that's all
we're using it for anyway
* integrate with stdexec's support for various ways of making senders
awaitable
* MOAR `std::unreachable()`
* support `unhandled_stopped()`
* Try a synthetic coroutine frame
This diff is inspired by the synthetic coroutine frame in @sgerbino's
fork of Capy, which I looked at here:
https://github.com/sgerbino/capy/blob/pr/beman-bench/bench/beman/awaitable_sender.hpp#L87-L92
The idea is to fake out a `coroutine_handle<__promise>` from a struct
like this:
```
struct __synthetic_coro_frame
{
void (*__resume)(void*) noexcept;
void (*__destroy_)(void*) noexcept;
};
```
and lie to the compiler with code like this:
```
// in __opstate<...>::__co_impl, which is a static member function
__std::coroutine_handle<__promise_t>::from_address(&__op.__synthetic_frame_);
```
With the right pointer math and a sprinkling of `reinterpret_cast`, this
code builds and passes all tests with Clang 22 and GCC 15 on Apple
silicon.
* Buy security at the cost of a pointer
The synthesized coroutine handle is passed to user code when it connects
an awaitable that suspends (it's received as the argument to
`await_suspend`), which means that user code can invoke `coro.destroy()`
even though we never do that in library code. The previous memory layout
stored `__opstate<...>::__started_` in the last byte of its synthesized
frame by artificially shortening the struct by a byte and marking the
`__started_` member with `[[no_unique_address]]`, which means that
`destroy` could point to a somewhat arbitrary address, depending on how
a `true` `bool` in the last byte is interpreted. This is potentially a
security hole so, to be safe, let's not squeeze so hard and just make
room for a function pointer and assign it the address of a no-op
function.
---------
Co-authored-by: Eric Niebler <eniebler@nvidia.com>1 parent 7c54331 commit 0646567
File tree
4 files changed
+1159
-135
lines changed- .github/workflows
- include/stdexec/__detail
- test
- stdexec/cpos
4 files changed
+1159
-135
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | | - | |
| 30 | + | |
0 commit comments