Tensions in Practice: Implicit return ownership in tension with ending the initiating call¶
Asynchronous work · one awaited event
A task pauses until an event arrives. A synchronous function can keep its call frame open and resume the remaining work there. An asynchronous design instead records that remaining work as an explicit object, returns from the initiating call, and later runs a callback. The work has not vanished; responsibility for resumption and cleanup has moved out of that call’s lifetime.
Keep return and cleanup nested
Let one open call scope own the pending continuation and its cleanup.
End the initiating call
Let the initiating function return while the event and remaining work are still outstanding.
Why these aims pull against each other
Returning early removes the live call frame that would otherwise track the obligation. The pending task must retain its own control state and cleanup responsibility.
Choose an arrangement to see what changes and what remains difficult.
Finite illustrative comparisons. Text states carry the meaning; color is not a measured score or universal preference.
What this choice protects
What it costs
When it fits
Compare the arrangements
Retain the call
The call stays open during the wait; the task resumes in that frame or unwinds it on failure.
| Call | Work | Cleanup | |
|---|---|---|---|
| Wait | Open | Call frame | Call scope |
| Event | Active | Same frame | Call scope |
| Failure | Unwinds | Stopped | Call scope |
- What it protects
- Return and failure cleanup stay attached to the same call scope.
- What it costs
- The original call cannot return during the wait and its pending state remains retained.
- When it fits
- Waiting within the call’s lifetime is acceptable and the required obligations genuinely nest.
Illustration note: The finite setting and values are editorial assumptions, not measured effects or recommended operating settings. “Waiting” does not assert that a physical processor spins or that an operating-system thread must remain busy.
Retain a task
Create a task object holding the continuation and cleanup responsibility, then return from the initiating call. An event handler later runs its callback.
| Call | Work | Cleanup | |
|---|---|---|---|
| Wait | Call ended | Work object | Task scope |
| Event | Call ended | Callback | Task scope |
| Failure | Call ended | Stopped | Task scope |
- What it protects
- The initiating call’s lifetime is no longer extended by the event wait.
- What it costs
- The task object needs explicit retention, dispatch and failure cleanup; unwinding the already-returned call cannot perform that cleanup.
- When it fits
- An event dispatcher and task-lifecycle contract can own the work after the initiating call has ended.
Illustration note: The finite setting and values are editorial assumptions, not measured effects or recommended operating settings. The task-scope cleanup contract is editorial and must be implemented; creating a callback does not supply it automatically.
What this illustration does—and does not—establish
Stack: Explicit Stack versus Reified Continuation (coupling) supplies reified continuation and the loss of automatic call-stack cleanup. The finite lifecycle table makes the changed owner explicit.
- Both options retain enough information to resume; no memory saving or timing guarantee is claimed.
- The table shows one event and one continuation, not a complete cancellation or coroutine implementation.
- Task failure means abandoning this remaining computation after its required cleanup; irreversible effects are not undone merely by either scope ending.
- Ending this call does not imply that its caller, thread or process has ended.
Source entries
Stack
Stack: Explicit Stack versus Reified Continuation (coupling) supplies the conflict examined here.
Explicit Stack versus Reified Continuation (coupling)
Stack discipline can be traded for explicit continuations or an event-driven work-list, decoupling the rest-of-computation from a single LIFO spine.
Structural Tensions
The failure is assuming stack-based reasoning (a clean caller/callee return) in a system that has gone event-driven — control returns to no caller, and the implicit unwinding the stack provided no longer happens.