Skip to content

Add resource-only scopes (resourceScope / ResourceScope)#480

Merged
adamw merged 10 commits into
masterfrom
resource-scopes
Jul 21, 2026
Merged

Add resource-only scopes (resourceScope / ResourceScope)#480
adamw merged 10 commits into
masterfrom
resource-scopes

Conversation

@adamw

@adamw adamw commented Jul 20, 2026

Copy link
Copy Markdown
Member

Adds resource scopes: scopes which allow attaching resources (useInScope / releaseAfterScope style), but are not full concurrency scopes — no forking, no threads started while the body runs. Every concurrency scope can be used where a resource scope is expected (via subtyping: OxUnsupervised extends ResourceScope). This is ox's analogue of scala.util.Using.Manager, and lets methods declare exactly the capability they need (using ResourceScope — attach cleanup, without claiming the ability to fork).

Design highlights (adversarially critiqued before implementation):

  • Compile-time guard: resourceScope cannot be started where a concurrency scope is lexically visible (via a NotGiven-based given with a custom error message) — forks started within a lexically visible resource scope could outlive it. The recommended structure is extracting the scope to a capability-free method.
  • Finalizer-list freeze: registering a resource after its scope ended (possible only via explicitly-leaked capabilities) now throws IllegalStateException instead of being silently lost; useInScope releases the just-acquired resource in that case, so cleanup is never lost. Applies uniformly to concurrency scopes.
  • Binary compatibility preserved: the four resource functions now take using ResourceScope; bridges with the old using OxUnsupervised JVM signatures are kept via @targetName (with distinct Scala-level names, avoiding overload ambiguity) — MiMa passes with no new filters. Source compatibility is unaffected.
  • Nesting/ForkLocal semantics pinned by tests: resources attach to the nearest enclosing scope; resource-scope finalizers see the fork-local values of the scope's own level.

🤖 Generated with Claude Code

adamw and others added 7 commits July 20, 2026 14:48
…pe end

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The bridges keep the old JVM signatures via @TargetNAME, with distinct
Scala-level names to avoid overload ambiguity at in-package call sites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…guard

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The NoEnclosingConcurrencyScope given moves to the type's companion, so
that it's found via the implicit scope without any imports - top-level
givens are not brought in by wildcard imports, which made resourceScope
unusable outside of the ox package (caught by the docs compilation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Parameterize the finalizer-error tests over both scope kinds, proving
  resourceScope's dedicated implementation shares runFinalizers semantics
- Defer the delegator scaladocs to useInScope (canonical contract),
  removing the 4x-repeated concurrency-scope sentence
- Align fork-completion wording with the dictionary (exception, not
  error); tighten the resourceScope scaladoc; drop restating comments
- Document runFinalizers' single-shot contract; drop unneeded inline

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@adamw

adamw commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

Release-note-worthy behavior change (surfaced in review): a finalizer which registers another finalizer on the same scope previously had the registration silently lost; it now fails the scope with an IllegalStateException (suppressed into the scope's result). No correct program could depend on the old behavior — the registered finalizer never ran.

adamw and others added 3 commits July 21, 2026 11:29
Per a test-minimality audit: reverse-order subsumes after-allocation;
the finalizer-error scenarios and the register-only case pin shared
runFinalizers logic, so a single scope kind suffices; the two
leaked-capability tests merge into one (throw + message + eager release).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@adamw
adamw merged commit fd71b52 into master Jul 21, 2026
6 checks passed
@adamw
adamw deleted the resource-scopes branch July 21, 2026 11:39
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