Split Msg into transport envelope and future - #3040
Open
plajjan wants to merge 1 commit into
Open
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 36b86adb72
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
plajjan
force-pushed
the
msg-future-split
branch
2 times, most recently
from
July 24, 2026 20:15
fe87339 to
4d19f72
Compare
One B_Msg object used to play three roles: the mailbox envelope carrying a call to an actor, that actor's activation frame while the call runs, and the future the caller awaits. Three roles with two different owners and two different lifetimes in one allocation. This splits it into two objects, and the names land where they belong: B_Msg now denotes the message itself — the transport envelope — and the awaitable becomes Future[A] / B_Future. - B_Msg is the transport envelope, a hand-written RTS type not exposed to Acton source: $next linkage, $to, $cont, $baseline, value, and a $fut link to the future it fulfills. The actor mailbox fields become $msg/$msg_tail/$msg_lock/$outgoing, typed B_Msg; the compiler's prim environment types those slots so generated actor headers agree with the RTS, and builtin.h forward-declares B_Msg for generated code. - The future keeps the surface role under its proper name: with the envelope split out, what remains of Msg[A] is precisely the future an async call returns, so the surface type becomes Future[A] and its C type B_Future. It keeps $waiting/$wait_lock (the waiter list) and gains an explicit $state (FUT_PENDING/FUT_VALUE/FUT_EXCEPTION) replacing the old encoding of result state in the $cont field. $ASYNC/$AFTER allocate one of each and link them via env->$fut; the dispatch loop freezes results into the future and wakes its waiters. The waiter helpers (ADD_waiting, FREEZE_waiting, $AWAIT) take the future. The rename is user-visible (source annotating Msg[A] must say Future[A]) but behavior-neutral: generated code treats the future opaquely, only calling $ASYNC/$AWAIT/$AFTER. The rename portions in the compiler passes, the lib-test fixtures, net.ext.c, function.h and __builtin__.act are mechanical token substitutions. In the class id table the future's id is FUTURE_ID and the envelope gets its own preassigned MSG_ID, like the other RTS types. With --db, both kinds of row live in MSGS_TABLE and recovery branches on the class id to allocate the right struct. serialize_msg/serialize_future are split accordingly. The dead $waitsfor persistence branch is dropped: it is provably NULL at every serialize point, since recovery replays a parked actor's whole turn from the envelope at its mailbox head. After envelopes carry no future at all: "after" is a statement, its value cannot be bound or awaited (the parser rejects it), so the future $AFTER used to allocate was bound by generated code into a dead temporary and dropped, once per timer. The AFTER prims are now typed to return None, $AFTER returns B_None and leaves $fut NULL, and the dispatch loop skips result delivery for a NULL future. Timer handling deals purely in envelopes. A dev-guide page (runtime/messages.md) documents the design: the two roles and lifetimes, why the per-actor memory model forces the separation, how $ASYNC links the two, and how the split shows up in DB persistence. The await semantics tests' docstrings are updated to the split terminology (waiter lists, freezing and result state belong to the future); the test bodies are unchanged.
plajjan
force-pushed
the
msg-future-split
branch
from
July 26, 2026 09:08
4d19f72 to
c135198
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.
One B_Msg object used to play three roles: the mailbox envelope carrying a call to an actor, that actor's activation frame while the call runs, and the future the caller awaits. Three roles with different owners and different lifetimes in one allocation. This splits it into two objects, and the names land where they belong: B_Msg now denotes the message itself, the transport envelope, while the awaitable becomes Future[A] with C type B_Future.
The envelope is a hand-written RTS type not exposed to Acton source. The future keeps the surface role: source annotating Msg[A] must now say Future[A], but generated code treats the type opaquely so there is no behavioral change. The future gains an explicit result state (pending, value, exception) instead of encoding it in the continuation field. An async call allocates one of each and links them; the dispatch loop freezes results into the future and wakes its waiters. Both objects serialize under their own class ids for the distributed backend, and a dead persistence branch for the awaited future is removed since recovery replays a parked actor's whole turn.
A new dev-guide page (runtime/messages.md) documents the design: the two roles and lifetimes, why the per-actor memory model forces the separation, and how the split shows up in DB persistence.
The await semantics tests carry docstrings describing the RTS paths they exercise; those are updated to the split terminology, with the test bodies untouched.