Skip to content

Collapse nested application-owned sharing beneath PreparedApp #648

Description

@leynos

Summary

Once PreparedApp is the deliberate shared root, remove inner Arc layers for application-owned values that cannot escape with an independent lifetime.

This is a follow-up to #641, #642, and #643. It should land only after the prepared-application and connection-runtime boundaries make each value's lifetime explicit.

Problem

WireframeApp currently stores several values behind Arc because connections may use them concurrently:

  • connection setup callback;
  • connection teardown callback;
  • message assembler;
  • protocol implementation;
  • route handlers and prepared route table.

Concurrency alone does not require each child value to own a separate reference count when one Arc<PreparedApp> already keeps the entire immutable object graph alive.

Some children may still need independent ownership:

  • a protocol handle exposed through a public clone-returning accessor;
  • a handler supplied as an externally shared Arc;
  • an application data value intentionally returned as an owned shared handle.

The task is therefore semantic classification, not mechanical replacement.

Scope

Lifecycle callbacks

Store setup and teardown callback definitions directly or behind Box<dyn ... + Send + Sync> inside PreparedApp when they are only borrowed during one connection lifecycle.

Do not clone callback owners per connection. The connection runtime should borrow the callback through its Arc<PreparedApp> root and own only the produced state C.

Message assembler

If the assembler is immutable and only called through &self, store it directly behind Box<dyn MessageAssembler> in PreparedApp and borrow &dyn MessageAssembler from connection runtimes.

Review the public message_assembler() accessor tracked by #598. Choose and document one disposition:

  • replace it with a borrowed Option<&dyn MessageAssembler> in a semver-appropriate release;
  • deprecate/remove it if no supported use case exists;
  • retain independent Arc ownership only if downstream code genuinely needs to clone the assembler out of the app.

Protocol definition

Coordinate with #644. Do not independently redesign protocol ownership here. The final graph should have no more protocol strong references than the accepted #644 representation requires.

Routes and handlers

Ensure the prepared route table is owned directly by PreparedApp, as required by #641.

Move handler ownership from the consumed builder into prepared services without an avoidable clone. Retain an Arc<Handler> only where the public handler value may have legitimate owners outside Wireframe or where middleware/service representation requires independent cloning.

Application data

Do not remove the per-value Arc contract from AppDataStore in this issue. SharedState<T> intentionally returns an owned handle that may outlive request extraction scope. Review map-level sharing separately only if benchmarks show repeated map cloning or contention.

Acceptance criteria

  • Every nested Arc field under PreparedApp has a documented independent-lifetime justification or is replaced by direct/boxed ownership.
  • Connection setup and teardown callback owners are not cloned per connection.
  • Message assembler use does not require an inner Arc unless the public API explicitly promises clone-out ownership.
  • The Mutation testing: untested public accessor survivors #598 accessor disposition is implemented and tested or deferred to a named breaking-release issue.
  • Handler ownership moves from the consumed builder into prepared route services without an avoidable clone.
  • Protocol ownership agrees with Consolidate protocol ownership and hook dispatch per connection #644 and is not duplicated here.
  • AppDataStore shared-value semantics remain intact.
  • No shared mutable callback or assembler state is introduced behind a mutex.
  • Drop-count tests prove application-owned values are released when the final PreparedApp root drops.
  • Establish runtime ownership and task-churn baselines #639 allocation benchmarks record the effect on application preparation and connection startup.

Tests

  • Use drop probes around setup, teardown, assembler, protocol, and handler objects to verify expected owner counts and final release.
  • Serve multiple concurrent connections and verify immutable callbacks/assembler calls remain thread-safe.
  • Assert setup returns separate connection-state values while sharing only callback definition.
  • Assert the assembler can serve interleaved connections without per-connection owner clones.
  • Preserve all public accessor tests retained from Mutation testing: untested public accessor survivors #598.

Non-goals

  • Removing legitimate shared handles from AppDataStore, PushHandle, Tokio channels, or semaphores.
  • Replacing externally shared route handlers merely to achieve an Arc-free field list.
  • Combining protocol and lifecycle mutable state.
  • Changing callback behaviour or ordering.

Dependencies

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    concurrencyConcurrency, parallelism, and synchronization work, including races and deadlocks.enhancementNew feature or requestlowBacklog work, hygiene, or technical debt planned opportunistically, without a strict deadline.mediumRoadmap items to schedule within the current quarter. Clear scope, normal review cycles.performancerefactorBehaviour-preserving restructuring that improves code health.testingTest coverage, test infrastructure, and verification tooling work.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions