EffectScope class abstract interface

An active execution context scope for a scoped Effect run.

Encapsulates the execution turn's lifecycle status flags and cleanup callback registration, safeguarding against in-flight race conditions and background zombie execution.

System Architectural Flow

Execution Turn State Machine

[Upstream Target Mutation / Turn Initialized]
                      │
                      ▼
       ┌──────────────────────────────┐
       │      EffectScope Active      │ (isActive == true, isCancelled == false)
       │ (Registers Cleanups via RAII)│
       └──────────────┬───────────────┘
                      │
                      ├─► [Upstream Mutates Again] ──┐
                      │                              │ (Invokes _dispose())
                      ├─► [Effect Terminal Deactivated]│
                      │                              │
                      ▼                              ▼
       ┌────────────────────────────────────────────────────────┐
       │                 EffectScope Disposed                   │ (isActive == false)
       │       (Executes Registered Cleanups in LIFO Order)     │ (isCancelled == true)
       └────────────────────────────────────────────────────────┘

Key Invariants & Architectural Benefits

  1. RAII Multi-Registration: Call onCleanup multiple times within a single turn. Cleanups are stored directly on the fast path (single cleanup) without heap list allocation, and composed recursively with try-finally chaining when multiple cleanups are added.
  2. Exception Safety during Setup: If acquiring a second resource throws an exception, previously registered cleanups for earlier resources remain intact and will be executed when the scope is disposed.
  3. Cancellation & Zombie Protection: Asynchronous or long-running tasks can inspect isActive and isCancelled to discard stale results if a newer mutation triggered.
  4. Immediate Teardown on Stale Registration: If onCleanup is invoked after this scope has already been cancelled, the cleanup executes immediately to prevent resource leaks.

Warning (Anti-Pattern Prevention):

  • Do NOT retain EffectScope references across multiple effect execution turns.
  • Do NOT omit cleanup registration for active timers, subscriptions, or sockets acquired inside scoped effect callbacks.

BAD:

// [Anti-Pattern] Unregistered timer continues running after effect teardown
Effect([room.bloom], () {
  Timer.periodic(const Duration(seconds: 1), (_) => ping()); // Leaks!
});

GOOD:

// [Recommended] Bind resource teardown directly to the execution scope
Effect.scoped([room.bloom], (scope) {
  final timer = Timer.periodic(const Duration(seconds: 1), (_) => ping());
  scope.onCleanup(timer.cancel);
});

Examples

final room = blooming('lobby');

final roomEffect = Effect.scoped([room.bloom], (scope) {
  final timer = Timer.periodic(const Duration(seconds: 5), (_) => sendHeartbeat());
  scope.onCleanup(timer.cancel);
});

Properties

hashCode → int
The hash code for this object.
no setterinherited
isActive → bool
Whether this execution turn is currently active and valid (neither cancelled nor deactivated).
no setter
isCancelled → bool
Whether this execution turn has been cancelled by a subsequent invalidation, node deactivation, or lifecycle teardown.
no setter
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited

Methods

noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
onCleanup(void cleanup()) → void
Registers a synchronous cleanup callback to be executed upon turn cancellation or deactivation.
toString() → String
A string representation of this object.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited