import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" import * as MutableRef from "effect/MutableRef" import * as Option from "effect/Option" import * as Scope from "effect/Scope" import { internalInterruptors } from "./interruptors.js" export type State = { readonly _tag: "Closed" } | { readonly _tag: "Acquiring" readonly scope: Scope.CloseableScope } | { readonly _tag: "Acquired" readonly scope: Scope.CloseableScope readonly value: A } export class ResourceRef { static from = Effect.fnUntraced(function*( parentScope: Scope.Scope, acquire: (scope: Scope.Scope) => Effect.Effect ) { const state = MutableRef.make>({ _tag: "Closed" }) yield* Scope.addFinalizerExit(parentScope, (exit) => { const s = MutableRef.get(state) if (s._tag === "Closed") { return Effect.void } const scope = s.scope MutableRef.set(state, { _tag: "Closed" }) return Scope.close(scope, exit) }) const scope = yield* Scope.make() MutableRef.set(state, { _tag: "Acquiring", scope }) const value = yield* acquire(scope) MutableRef.set(state, { _tag: "Acquired", scope, value }) return new ResourceRef(state, acquire) }) constructor( readonly state: MutableRef.MutableRef>, readonly acquire: (scope: Scope.Scope) => Effect.Effect ) {} latch = Effect.unsafeMakeLatch(true) unsafeGet(): Option.Option { if (this.state.current._tag === "Acquired") { return Option.some(this.state.current.value) } return Option.none() } unsafeRebuild(): Effect.Effect { const s = this.state.current if (s._tag === "Closed") { return Effect.interrupt } const prevScope = s.scope const scope = Effect.runSync(Scope.make()) this.latch.unsafeClose() MutableRef.set(this.state, { _tag: "Acquiring", scope }) return Effect.fiberIdWith((fiberId) => { internalInterruptors.add(fiberId) return Scope.close(prevScope, Exit.void) }).pipe( Effect.andThen(this.acquire(scope)), Effect.flatMap((value) => { if (this.state.current._tag === "Closed") { return Effect.interrupt } MutableRef.set(this.state, { _tag: "Acquired", scope, value }) return this.latch.open }) ) } await: Effect.Effect = Effect.suspend(() => { const s = this.state.current if (s._tag === "Closed") { return Effect.interrupt } else if (s._tag === "Acquired") { return Effect.succeed(s.value) } return Effect.zipRight(this.latch.await, this.await) }) }