⚡ (patch) Hoist promise's future_base* owner, delete per-T cancel indirection - #109
Merged
Conversation
…irection promise_base now stores a future_base* (m_owner) instead of relying on a per-T cancel_promise<T> static function plus an m_cancel function pointer. cancel() and unhandled_exception() only ever touch future_base's fields (m_tag, m_base), never anything T-specific, so both move from promise<T> to promise_base as ordinary non-template member functions - one shared compiled instance instead of one per T, and no more m_cancel store emitted in every coroutine's ramp. promise_return_base<T>::m_owner (future<T>*) stays separate and templated, since return_value()/return_void() write into future<T>'s own m_storage, which future_base doesn't have. Measured on the usb demo (stm32f103zg, clang 20, MinSizeRel): 65,069 -> 64,821 bytes of flash (-248 B), verified via a full libhal -> libhal-util -> libhal-usb -> libhal-arm-mcu -> demos rebuild. All 12 async_context tests pass.
promise_base::m_owner (future_base*) and promise_return_base<T>::m_owner (future<T>*) were pointing at the same object from two separate fields, one per promise<T> - promise_return_base<T> is a sibling base of promise_base (both are direct bases of promise<T>), not derived from it, so it couldn't reach promise_base's pointer without its own copy. Since promise_return_base<T> is only ever used as a base of promise<T>, return_value()/return_void() can reach promise_base::m_owner safely via a static_cast through promise<T> (added as a friend of promise_base for this), then downcast to future<T>*/future<void>* to get at m_storage. Removes the second field (4 B/frame RAM) and the redundant second store at each of the three future<T> construction/move call sites. Measured on the usb demo (stm32f103zg, clang 20, MinSizeRel): 64,821 -> 64,757 bytes of flash (-64 B), on top of the -248 B already landed in this PR. All 12 async_context tests pass.
kammce
force-pushed
the
hoist-promise-owner
branch
from
September 3, 2026 23:24
7cfb170 to
24c02d9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
promise_basenow stores afuture_base*(m_owner) instead of routing cancellation through a per-Tcancel_promise<T>static function plus anm_cancelfunction pointer.cancel()andunhandled_exception()only ever touchfuture_base's fields (m_tag,m_base), never anythingT-specific, so both move frompromise<T>topromise_baseas ordinary non-template member functions — one shared compiled instance instead of one perT, and no morem_cancelstore emitted in every coroutine's ramp.promise_return_base<T>'s own, separatem_owner(future<T>*) is then eliminated too: sincepromise_return_base<T>is only ever used as a base ofpromise<T>,return_value()/return_void()reachpromise_base::m_ownervia astatic_castthroughpromise<T>(added as a friend ofpromise_basefor this) and downcast tofuture<T>*/future<void>*, instead of keeping a second, redundant pointer.Measured on the
usbdemo (stm32f103zg, clang 20, MinSizeRel), rebuilt end-to-end throughlibhal→libhal-util→libhal-usb→libhal-arm-mcu→ demos:m_ownerTest plan
async_contextunit tests pass (ctest), includingtest_cancelandtest_cross_context_awaitlibhal,libhal-util,libhal-usb,libhal-arm-mcu, demos) succeedsusbdemo binary flash size measured before/after each commit on identical toolchain/profile