Skip to content

Commit 0646567

Browse files
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

4 files changed

+1159
-135
lines changed

.github/workflows/test-windows.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Invoke-NativeCommand cmake -B $BuildDirectory -G Ninja `
2424
"-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT:STRING=Embedded" `
2525
"-DSTDEXEC_ENABLE_ASIO:BOOL=TRUE" `
2626
"-DSTDEXEC_ASIO_IMPLEMENTATION:STRING=boost" `
27+
"-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" `
2728
"-DSTDEXEC_BUILD_TESTS:BOOL=TRUE" .
2829
Invoke-NativeCommand cmake --build $BuildDirectory
29-
Invoke-NativeCommand ctest --test-dir $BuildDirectory
30+
Invoke-NativeCommand ctest --test-dir $BuildDirectory --output-on-failure --verbose --timeout 60

0 commit comments

Comments
 (0)