"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.interruptible = exports.interrupt = exports.interruptAs = exports.interruptibleMask = exports.disconnect = exports.onInterrupt = exports.onInterruptExtended_ = exports.onInterrupt_ = exports.uninterruptibleMask = exports.InterruptStatusRestoreImpl = exports.uninterruptible = void 0;
// ets_tracing: off
const Cause = __importStar(require("../Cause/core"));
const Fiber = __importStar(require("../Fiber/core"));
const core_1 = require("../Fiber/core");
const core_2 = require("./core");
const core_scope_1 = require("./core-scope");
const fiberId_1 = require("./fiberId");
/**
 * Performs this effect uninterruptibly. This will prevent the effect from
 * being terminated externally, but the effect may fail for internal reasons
 * (e.g. an uncaught error) or terminate due to defect.
 *
 * Uninterruptible effects may recover from all failure causes (including
 * interruption of an inner effect that has been made interruptible).
 */
exports.uninterruptible = (0, core_2.interruptStatus)(core_1.uninterruptible);
class InterruptStatusRestoreImpl {
    constructor(flag) {
        this.flag = flag;
        this.restore = this.restore.bind(this);
        this.force = this.force.bind(this);
    }
    restore(effect, __trace) {
        return (0, core_2.interruptStatus_)(effect, this.flag, __trace);
    }
    force(effect, __trace) {
        if (this.flag.isUninteruptible) {
            return interruptible(disconnect((0, exports.uninterruptible)(effect)), __trace);
        }
        return (0, core_2.interruptStatus_)(effect, this.flag, __trace);
    }
}
exports.InterruptStatusRestoreImpl = InterruptStatusRestoreImpl;
/**
 * Makes the effect uninterruptible, but passes it a restore function that
 * can be used to restore the inherited interruptibility from whatever region
 * the effect is composed into.
 */
function uninterruptibleMask(f, __trace) {
    return (0, core_2.checkInterruptible)((flag) => (0, exports.uninterruptible)(f(new InterruptStatusRestoreImpl(flag))), __trace);
}
exports.uninterruptibleMask = uninterruptibleMask;
/**
 * Calls the specified function, and runs the effect it returns, if this
 * effect is interrupted.
 */
function onInterrupt_(self, cleanup, __trace) {
    return uninterruptibleMask(({ restore }) => (0, core_2.foldCauseM_)(restore(self), (cause) => Cause.interrupted(cause)
        ? (0, core_2.chain_)(cleanup(Cause.interruptors(cause)), () => (0, core_2.halt)(cause))
        : (0, core_2.halt)(cause), core_2.succeed, __trace));
}
exports.onInterrupt_ = onInterrupt_;
/**
 * Calls the specified function, and runs the effect it returns, if this
 * effect is interrupted (allows for expanding error).
 */
function onInterruptExtended_(self, cleanup, __trace) {
    return uninterruptibleMask(({ restore }) => (0, core_2.foldCauseM_)(restore(self), (cause) => Cause.interrupted(cause)
        ? (0, core_2.foldCauseM_)(cleanup(Cause.interruptors(cause)), (_) => (0, core_2.halt)(_), () => (0, core_2.halt)(cause))
        : (0, core_2.halt)(cause), core_2.succeed, __trace));
}
exports.onInterruptExtended_ = onInterruptExtended_;
/**
 * Calls the specified function, and runs the effect it returns, if this
 * effect is interrupted.
 *
 * @ets_data_first onInterrupt_
 */
function onInterrupt(cleanup, __trace) {
    return (self) => onInterrupt_(self, cleanup, __trace);
}
exports.onInterrupt = onInterrupt;
/**
 * Returns an effect whose interruption will be disconnected from the
 * fiber's own interruption, being performed in the background without
 * slowing down the fiber's interruption.
 *
 * This method is useful to create "fast interrupting" effects. For
 * example, if you call this on a bracketed effect, then even if the
 * effect is "stuck" in acquire or release, its interruption will return
 * immediately, while the acquire / release are performed in the
 * background.
 *
 * See timeout and race for other applications.
 */
function disconnect(effect, __trace) {
    return uninterruptibleMask(({ restore }) => (0, core_2.chain_)(fiberId_1.fiberId, (id) => (0, core_2.chain_)((0, core_scope_1.forkDaemon)(restore(effect)), (fiber) => onInterrupt_(restore(Fiber.join(fiber)), () => (0, core_scope_1.forkDaemon)(fiber.interruptAs(id))))), __trace);
}
exports.disconnect = disconnect;
/**
 * Makes the effect interruptible, but passes it a restore function that
 * can be used to restore the inherited interruptibility from whatever region
 * the effect is composed into.
 */
function interruptibleMask(f, __trace) {
    return (0, core_2.checkInterruptible)((flag) => interruptible(f(new InterruptStatusRestoreImpl(flag))), __trace);
}
exports.interruptibleMask = interruptibleMask;
/**
 * Returns an effect that is interrupted as if by the specified fiber.
 */
function interruptAs(fiberId, __trace) {
    return (0, core_2.haltWith)((trace) => Cause.traced(Cause.interrupt(fiberId), trace()), __trace);
}
exports.interruptAs = interruptAs;
/**
 * Returns an effect that is interrupted by the current fiber
 */
exports.interrupt = (0, core_2.chain_)(fiberId_1.fiberId, interruptAs);
/**
 * Returns a new effect that performs the same operations as this effect, but
 * interruptibly, even if composed inside of an uninterruptible region.
 *
 * Note that effects are interruptible by default, so this function only has
 * meaning if used within an uninterruptible region.
 *
 * WARNING: This operator "punches holes" into effects, allowing them to be
 * interrupted in unexpected places. Do not use this operator unless you know
 * exactly what you are doing. Instead, you should use `uninterruptibleMask`.
 */
function interruptible(effect, __trace) {
    return (0, core_2.interruptStatus_)(effect, core_1.interruptible, __trace);
}
exports.interruptible = interruptible;
//# sourceMappingURL=interruption.js.map