"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.effectMaybeAsyncInterruptBlockingOn = exports.effectMaybeAsyncInterrupt = void 0
const O = __importStar(require("../Option"))
const AtomicReference_1 = require("../Support/AtomicReference")
const OneShot_1 = require("../Support/OneShot")
const core = __importStar(require("./core"))
const flatten_1 = require("./flatten")
const interruption_1 = require("./interruption")
/**
 * Imports an asynchronous side-effect into an effect. The side-effect
 * has the option of returning the value synchronously, which is useful in
 * cases where it cannot be determined if the effect is synchronous or
 * asynchronous until the side-effect is actually executed. The effect also
 * has the option of returning a canceler, which will be used by the runtime
 * to cancel the asynchronous effect if the fiber executing the effect is
 * interrupted.
 *
 * If the register function returns a value synchronously, then the callback
 * function must not be called. Otherwise the callback function must be called
 * at most once.
 *
 * The list of fibers, that may complete the async callback, is used to
 * provide better diagnostics.
 */
function effectMaybeAsyncInterrupt(register, __trace) {
  return effectMaybeAsyncInterruptBlockingOn(register, [], __trace)
}
exports.effectMaybeAsyncInterrupt = effectMaybeAsyncInterrupt
/**
 * Imports an asynchronous side-effect into an effect. The side-effect
 * has the option of returning the value synchronously, which is useful in
 * cases where it cannot be determined if the effect is synchronous or
 * asynchronous until the side-effect is actually executed. The effect also
 * has the option of returning a canceler, which will be used by the runtime
 * to cancel the asynchronous effect if the fiber executing the effect is
 * interrupted.
 *
 * If the register function returns a value synchronously, then the callback
 * function must not be called. Otherwise the callback function must be called
 * at most once.
 *
 * The list of fibers, that may complete the async callback, is used to
 * provide better diagnostics.
 */
function effectMaybeAsyncInterruptBlockingOn(register, blockingOn, __trace) {
  return core.chain_(
    core.succeedWith(() => [
      new AtomicReference_1.AtomicReference(false),
      new OneShot_1.OneShot()
    ]),
    ([started, cancel]) =>
      (0, interruption_1.onInterrupt_)(
        (0, flatten_1.flatten)(
          core.effectAsyncOptionBlockingOn(
            (k) => {
              started.set(true)
              const ret = new AtomicReference_1.AtomicReference(O.none)
              try {
                const res = register((io) => k(core.succeed(io)))
                switch (res._tag) {
                  case "Right": {
                    ret.set(O.some(core.succeed(res.right)))
                    break
                  }
                  case "Left": {
                    cancel.set(res.left)
                    break
                  }
                }
              } finally {
                if (!cancel.isSet()) {
                  cancel.set(core.unit)
                }
              }
              return ret.get
            },
            blockingOn,
            __trace
          )
        ),
        () => core.suspend(() => (started.get ? cancel.get() : core.unit))
      )
  )
}
exports.effectMaybeAsyncInterruptBlockingOn = effectMaybeAsyncInterruptBlockingOn
//# sourceMappingURL=effectMaybeAsyncInterrupt.js.map
