Skip to content

fix: own actor shutdown and settle request/reply waits - #65

Merged
dbrattli merged 7 commits into
mainfrom
fix/actor-lifecycle-liveness
Oct 7, 2026
Merged

dbrattli merged 7 commits into
mainfrom
fix/actor-lifecycle-liveness

Conversation

@dbrattli

@dbrattli dbrattli commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Actors could continue running after kill, and a request could wait forever after its handler crashed or its target stopped. This change connects spawned work to an owned lifetime, adds awaitable shutdown, and makes request/reply waits settle on supported termination and cancellation paths across .NET, JavaScript, Python, and BEAM.

Integration plan — awaiting upstream maintainer review

The chosen integration order is to keep this PR in draft while the upstream runtime/compiler fixes await review by the Fable maintainers. This is an integration decision, not a failing build dependency: the Actor suite and CI pass against published Fable/fable-library 5.19.0 using narrow actor-local workarounds. Those tests cover the library's stated contract; they do not establish correct cancellation and cleanup for arbitrary supplied Async computations.

  • Fable #5035: Python per-object locks — merged.
  • Fable #5036: cancellation registration lifecycle — awaiting review/merge.
  • Fable #5037: generic unit arguments and Python unit continuations — awaiting review/merge.
  • Fable #5038: cancelled delays and finalizer settlement — awaiting review/merge; stacked on #5036. GitHub currently shows title-lint checks only for this PR; its description reports local runtime validation. Run the full upstream CI when it is retargeted to main.

Under this plan, wait for the relevant fixes to be reviewed by maintainers, merged, and published before merging this Actor PR. Update the compiler and matching runtime pins together, remove or adapt the workarounds that those releases supersede, inspect the revised cancellation boundary, and rerun the complete .NET/JavaScript/Python/BEAM suite plus formatting, builds, and packaging. Retain actor-level ownership and per-call settlement logic: upstream Async fixes do not replace these library responsibilities.

Behavior

  • spawn, spawnLinked, and spawnWithToken connect cancellation to cooperative work on emulated targets. Parent exit shuts down owned linked children, including after normal completion. Repeated and concurrent shutdown requests share cleanup.
  • stop/stopAsync validate a positive cleanup deadline before requesting shutdown. Completion observes workflow, cleanup, and owned-descendant exit on emulated targets, or native process death on BEAM. Cleanup failures and timeouts are reported.
  • callResult/callResultAsync distinguish reply, timeout, target termination, and explicit caller cancellation. callAsyncWithTimeout adds a bounded Async convenience API; existing unbounded call defaults remain.
  • Calls settle once and release their timers, registrations, and observers/monitors. BEAM aliases reject late replies; emulated reply callbacks become inert. Requests are never automatically retried. Timeout and cancellation do not retract delivered work or establish its processing outcome.
  • Downstream disposal guidance explains how to own a supervision tree, retain replacement children, cancel pending calls, and await actual worker termination.

Compatibility and target limits

  • Emulated Actor handles are internally constructed classes rather than public records. Existing Mb, Cts, and Pid access remains, but direct record construction is a breaking change.
  • Legacy calls now report target termination instead of waiting indefinitely. Timeout APIs require positive deadlines and deliver when the operation runs.
  • Parent ownership is stronger than a raw BEAM link. Native symmetric abnormal-exit behavior and emulated child-crash notification retain their differences; BEAM links now use the supplied parent.
  • Emulated calls settle when shutdown closes request admission; await stop separately for cleanup completion. Cancellation is cooperative, and synchronous work can delay deadline observation.
  • BEAM stop uses native kill: it cannot run finalizers or confirm descendant death at the instant the parent dies. Await each current worker when disposal requires the whole tree to be dead.
  • Python actor-owned registrations, locks, cancellation guards, and deadline timers use narrow adapters. Arbitrary supplied Async work retains upstream runtime behavior, including retained Async.Sleep callbacks. Source TODO(upstream) comments link the relevant Fable fixes and state the conditions for retiring each workaround.

The rc.11 baseline still had the lifecycle and call gaps; rc.12 had already fixed the supervised replacement-child return value. This PR preserves that API, upgrades Fable.Beam to 5.1.0 and both Python runtime locks to 5.19.0, and retains main's Fable 5.19.0 and Fantomas 8.0.6 pins. Dependency, shutdown, call, and documentation changes remain separate commits.

Validation

After rebasing onto current main:

  • .NET, JavaScript, and Python: 56 tests passed each; BEAM: 50 passed.
  • Deterministic startup, delivery, exit, and cleanup barriers cover replies, crashes, idle/busy shutdown, cancellation, concurrent/repeated stop, restart then shutdown, deadlines, pending-call disposal, late replies, and resource cleanup. Exit checks establish actual termination separately from notification suppression.
  • Formatting, just check, standalone BEAM just build, just pack, and git diff --check passed.

Local validation uses .NET SDK 10.0.400, Node 26.8.2, Python 3.14.0, and OTP 25; CI uses Node 20, Python 3.12, and OTP 27. Existing formatter, generated Erlang, and missing package-readme warnings remain.

@dbrattli dbrattli changed the title Fix/actor lifecycle liveness fix: Actor lifecycle liveness Oct 7, 2026
@dbrattli
dbrattli force-pushed the fix/actor-lifecycle-liveness branch from 3f03d39 to 1270b10 Compare October 7, 2026 19:21
@dbrattli dbrattli changed the title fix: Actor lifecycle liveness fix!: own actor shutdown and settle request/reply waits Oct 7, 2026
@dbrattli
dbrattli marked this pull request as draft October 7, 2026 19:27
@dbrattli
dbrattli force-pushed the fix/actor-lifecycle-liveness branch from c9c57ca to 832bc86 Compare October 7, 2026 19:54
@dbrattli dbrattli changed the title fix!: own actor shutdown and settle request/reply waits fix: own actor shutdown and settle request/reply waits Oct 7, 2026
@dbrattli
dbrattli marked this pull request as ready for review October 7, 2026 20:32
@dbrattli
dbrattli merged commit c941994 into main Oct 7, 2026
2 checks passed
@dbrattli
dbrattli deleted the fix/actor-lifecycle-liveness branch October 7, 2026 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant