import { pipe } from "../../Function"; import type { Exit } from "../Exit"; import { makeRef } from "../XRef/_core"; import * as T from "./_internal/task"; import { Managed } from "./model"; import type { Finalizer, ReleaseMap } from "./ReleaseMap"; import { add, addIfOpen, noopFinalizer, release } from "./ReleaseMap"; /** * Lift a pure value into a task */ export const succeed = (a: A) => fromTask(T.pure(a)); /** * Lifts a `Task` into `Managed` with no release action. The * effect will be performed interruptibly. */ export const fromTask = (effect: T.Task) => new Managed( T.map_( T.asksM((_: readonly [R, ReleaseMap]) => T.giveAll_(effect, _[0])), (a) => [noopFinalizer, a] ) ); /** * Imports a synchronous side-effect into a pure value */ export const total = (effect: () => A) => fromTask(T.total(effect)); /** * Returns a task that models failure with the specified error. The moral equivalent of throw for pure code. */ export const fail = (e: E) => fromTask(T.fail(e)); /** * Creates a task that executes a finalizer stored in a `Ref`. * The `Ref` is yielded as the result of the effect, allowing for * control flows that require mutating finalizers. */ export const finalizerRef = (initial: Finalizer) => makeExit_(makeRef(initial), (ref, exit) => T.chain_(ref.get, (f) => f(exit))); /** * Lifts a `Task` into `Managed` with a release action. * The acquire and release actions will be performed uninterruptibly. */ export const make = ( release: (a: A) => T.Task ): ((acquire: T.Task) => Managed) => makeExit((a) => release(a)); /** * Lifts a `Task` into `Managed` with a release action. * The acquire and release actions will be performed uninterruptibly. */ export const make_ = ( acquire: T.Task, release: (a: A) => T.Task ): Managed => makeExit_(acquire, (a) => release(a)); /** * Lifts a `Task` into `Managed` with a release action * that handles `Exit`. The acquire and release actions will be performed uninterruptibly. */ export const makeExit = (release: (a: A, exit: Exit) => T.Task) => ( acquire: T.Task ) => makeExit_(acquire, release); /** * Lifts a `Task` into `Managed` with a release action * that handles `Exit`. The acquire and release actions will be performed uninterruptibly. */ export const makeExit_ = ( acquire: T.Task, release: (a: A, exit: Exit) => T.Task ) => new Managed( T.makeUninterruptible( pipe( T.do, T.bindS("r", () => T.ask()), T.bindS("a", (s) => T.giveAll_(acquire, s.r[0])), T.bindS("rm", (s) => add((ex) => T.giveAll_(release(s.a, ex), s.r[0]))(s.r[1])), T.map((s) => [s.rm, s.a]) ) ) ); /** * Creates a `Managed` from a `Reservation` produced by a task. Evaluating * the effect that produces the reservation will be performed *uninterruptibly*, * while the acquisition step of the reservation will be performed *interruptibly*. * The release step will be performed uninterruptibly as usual. * * This two-phase acquisition allows for resource acquisition flows that can be * safely interrupted and released. */ export const makeReserve = (reservation: T.Task>) => new Managed( T.uninterruptibleMask(({ restore }) => pipe( T.do, T.bindS("tp", () => T.ask()), T.letS("r", (s) => s.tp[0]), T.letS("releaseMap", (s) => s.tp[1]), T.bindS("reserved", (s) => T.giveAll_(reservation, s.r)), T.bindS("releaseKey", (s) => addIfOpen((x) => T.giveAll_(s.reserved.release(x), s.r))(s.releaseMap)), T.bindS("finalizerAndA", (s) => { const k = s.releaseKey; switch (k._tag) { case "None": { return T.interrupt; } case "Some": { return T.map_( restore(T.local_(s.reserved.acquire, ([r]: readonly [R & R2, ReleaseMap]) => r)), (a): [Finalizer, A] => [(e) => release(k.value, e)(s.releaseMap), a] ); } } }), T.map((s) => s.finalizerAndA) ) ) ); /** * A `Reservation` encapsulates resource acquisition and disposal * without specifying when or how that resource might be used. */ export class Reservation { static of = (acquire: T.Task, release: (exit: Exit) => T.Task) => new Reservation(acquire, release); private constructor( readonly acquire: T.Task, readonly release: (exit: Exit) => T.Task ) {} } /** * Make a new reservation */ export const makeReservation_ = ( acquire: T.Task, release: (exit: Exit) => T.Task ) => Reservation.of(acquire, release); /** * Make a new reservation */ export const makeReservation = (release: (exit: Exit) => T.Task) => ( acquire: T.Task ) => Reservation.of(acquire, release); /** * Lifts a pure `Reservation` into `Managed`. The acquisition step * is performed interruptibly. */ export const reserve = (reservation: Reservation) => makeReserve(T.pure(reservation));