start method

Future<void> start()

Resolves all features in dependency order and starts the container.

Idempotent and queue-friendly with stop:

  • Calling start while a start is already in flight returns the same future (concurrent calls coalesce).
  • Calling start when the container is already in ContainerStatus.working (and no stop is pending) is a no-op.
  • Calling start while a stop is in flight queues the start — it begins after the stop completes, on the next cycle's fresh stores.

Throws ContainerUsageError if invoked from within a feature lifecycle callback (would self-deadlock).

Implementation

Future<void> start() {
  // Re-entrance guard runs first — before coalesce — because the
  // in-flight start IS the caller (a feature callback re-entering
  // start()). Coalescing onto the in-flight future would have the
  // callback await its own parent and deadlock.
  if (Zone.current[userCallbackZoneKey] == true) {
    return Future.error(
      ContainerUsageError(
        'AppContainer.start() cannot be called from within a feature '
        'lifecycle callback. Schedule the start outside of it '
        '(e.g. unawaited(Future.microtask(container.start))).',
      ),
    );
  }

  // Coalesce: an in-flight (or queued-after-stop) start returns the
  // same future. Two consecutive `container.start()` calls share one
  // result — the second doesn't kick off a second cycle.
  final inFlight = _startFuture;
  if (inFlight != null) return inFlight;

  // No-op: container is already up and no stop is tearing it down.
  // Returns an already-completed future so callers can `await` without
  // ceremony.
  if (_status == ContainerStatus.working && _stopping == null) {
    return Future.value();
  }

  assert(() {
    // Re-attach the debug finalizer for this cycle. `detach` first
    // is defensive — if a previous cycle didn't detach (e.g. because
    // it was never stopped), we'd otherwise stack attach calls.
    _debugFinalizer.detach(this);
    _debugFinalizer.attach(
      this,
      'AppContainer#${identityHashCode(this)}',
      detach: this,
    );
    return true;
  }());
  return _startFuture = _runStart();
}