UNPKG

@effect-ts/system

Version:

Effect-TS is a zero dependency set of libraries to write highly productive, purely functional TypeScript at scale.

386 lines (337 loc) 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultPool = void 0; exports.fromIterable = fromIterable; exports.get = get; exports.invalidate = invalidate; exports.invalidate_ = invalidate_; exports.make = make; exports.makeFixed = makeFixed; exports.makeWith = makeWith; var CS = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Cause/index.js")); var HS = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Collections/Immutable/HashSet/index.js")); var Tp = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Collections/Immutable/Tuple/index.js")); var T = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Effect/index.js")); var Ex = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Exit/index.js")); var F = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Fiber/index.js")); var _index7 = /*#__PURE__*/require("../Function/index.js"); var M = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Managed/index.js")); var Q = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Queue/index.js")); var Ref = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Ref/index.js")); var AT = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./Attempted.js")); var STR = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./Strategy.js")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // ets_tracing: off class Pool {} T._E, T._A; class PoolInternal extends Pool {} T._E, T._A; function concrete(pool) {// } function get(self) { concrete(self); return self.get(); } function invalidate_(self, item) { concrete(self); return self.invalidate(item); } function invalidate(item) { return self => invalidate_(self, item); } class DefaultPool extends PoolInternal { constructor(creator, range, isShuttingDown, state, items, invalidated, track) { super(); this.creator = creator; this.range = range; this.isShuttingDown = isShuttingDown; this.state = state; this.items = items; this.invalidated = invalidated; this.track = track; this.excess = this.excess.bind(this); this.get = this.get.bind(this); this.initialize = this.initialize.bind(this); this.invalidate = this.invalidate.bind(this); this.shrink = this.shrink.bind(this); this.allocate = this.allocate.bind(this); this.getAndShutdown = this.getAndShutdown.bind(this); this.shutdown = this.shutdown.bind(this); } /** * Returns the number of items in the pool in excess of the minimum size. */ excess() { return T.map_(this.state.get, ({ free, size }) => size - Math.min(Tp.get_(this.range, 0), free)); } get() { const acquire = T.chain_(this.isShuttingDown.get, down => { if (down) { return T.interrupt; } else { return T.flatten(Ref.modify_(this.state, ({ free, size }) => { if (free > 0 || size >= Tp.get_(this.range, 1)) { return Tp.tuple(T.chain_(Q.take(this.items), acquired => { if (acquired.result._tag === "Success") { const item = acquired.result.value; return T.chain_(this.invalidated.get, set => { if (HS.has_(set, item)) { return T.zipRight_(T.zipRight_(Ref.update_(this.state, state => ({ ...state, free: state.free + 1 })), this.allocate()), acquire); } else { return T.succeed(acquired); } }); } else { return T.succeed(acquired); } }), { size, free: free - 1 }); } else if (size >= 0) { return Tp.tuple(T.zipRight_(this.allocate(), acquire), { size: size + 1, free: free + 1 }); } else { return Tp.tuple(T.interrupt, { size, free }); } })); } }); const release = attempted => { if (AT.isFailure(attempted)) { return T.flatten(Ref.modify_(this.state, ({ free, size }) => { if (size <= Tp.get_(this.range, 0)) { return Tp.tuple(this.allocate(), { size, free: free + 1 }); } else { return Tp.tuple(T.unit, { size: size - 1, free }); } })); } else { return T.zipRight_(T.zipRight_(T.zipRight_(Ref.update_(this.state, state => ({ ...state, free: state.free + 1 })), Q.offer_(this.items, attempted)), this.track(attempted.result)), T.whenM_(this.getAndShutdown(), this.isShuttingDown.get)); } }; return M.chain_(M.make_(acquire, release), AT.toManaged); } /** * Begins pre-allocating pool entries based on minimum pool size. */ initialize() { return T.replicateMUnit_(T.uninterruptibleMask(({ restore }) => T.flatten(Ref.modify_(this.state, ({ free, size }) => { if (size < Tp.get_(this.range, 0) && size >= 0) { return Tp.tuple(T.map_(T.tap_(T.tap_(T.tap_(T.bind_(T.bind_(T.bind_(T.do, "reservation", () => M.managedReserve(this.creator)), "exit", ({ reservation }) => T.result(restore(reservation.acquire))), "attempted", ({ exit, reservation }) => T.succeed(new AT.Attempted(exit, reservation.release(Ex.succeed(undefined))))), ({ attempted }) => Q.offer_(this.items, attempted)), ({ attempted }) => this.track(attempted.result)), () => T.whenM_(this.getAndShutdown(), this.isShuttingDown.get)), ({ attempted }) => attempted), { size: size + 1, free: free + 1 }); } else { return Tp.tuple(T.unit, { size, free }); } }))), Tp.get_(this.range, 0)); } invalidate(item) { return Ref.update_(this.invalidated, _ => HS.add_(_, item)); } /** * Shrinks the pool down, but never to less than the minimum size. */ shrink() { return T.uninterruptible(T.flatten(Ref.modify_(this.state, ({ free, size }) => { if (size > Tp.get_(this.range, 0) && free > 0) { return Tp.tuple(T.chain_(Q.take(this.items), attempted => T.zipRight_(T.zipRight_(AT.forEachUnit(a => Ref.update_(this.invalidated, _ => HS.remove_(_, a)))(attempted), attempted.finalizer), Ref.update_(this.state, state => ({ ...state, size: state.size - 1 })))), { size, free: free - 1 }); } else { return Tp.tuple(T.unit, { size, free }); } }))); } allocate() { return T.uninterruptibleMask(({ restore }) => T.map_(T.tap_(T.tap_(T.tap_(T.bind_(T.bind_(T.bind_(T.do, "reservation", () => M.managedReserve(this.creator)), "exit", ({ reservation }) => T.result(restore(reservation.acquire))), "attempted", ({ exit, reservation }) => T.succeed(new AT.Attempted(exit, reservation.release(Ex.succeed(undefined))))), ({ attempted }) => Q.offer_(this.items, attempted)), ({ attempted }) => this.track(attempted.result)), () => T.whenM_(this.getAndShutdown(), this.isShuttingDown.get)), ({ attempted }) => attempted)); } /** * Gets items from the pool and shuts them down as long as there are items * free, signalling shutdown of the pool if the pool is empty. */ getAndShutdown() { return T.flatten(Ref.modify_(this.state, ({ free, size }) => { if (free > 0) { return Tp.tuple(T.foldCauseM_(Q.take(this.items), _ => T.unit, attempted => T.zipRight_(T.zipRight_(T.zipRight_(AT.forEachUnit(a => Ref.update_(this.invalidated, _ => HS.remove_(_, a)))(attempted), attempted.finalizer), Ref.update_(this.state, state => ({ ...state, size: state.size - 1 }))), this.getAndShutdown())), { size, free: free - 1 }); } else if (size > 0) { return Tp.tuple(T.unit, { size, free }); } else { return Tp.tuple(Q.shutdown(this.items), { size: size - 1, free }); } })); } shutdown() { return T.flatten(Ref.modify_(this.isShuttingDown, down => { if (down) { return Tp.tuple(Q.awaitShutdown(this.items), true); } else { return Tp.tuple(T.zipRight_(this.getAndShutdown(), Q.awaitShutdown(this.items)), true); } })); } } /** * Creates a pool from a fixed number of pre-allocated items. This method * should only be used when there is no cleanup or release operation * associated with items in the pool. If cleanup or release is required, * then the `make` constructor should be used instead. */ exports.DefaultPool = DefaultPool; function fromIterable(iterable0) { return M.map_(M.bind_(M.let_(M.bind_(M.bind_(M.do, "iterable", () => M.succeed(Array.from(iterable0))), "source", ({ iterable }) => T.toManaged(Ref.makeRef(iterable))), "get", ({ iterable, source }) => { if (!iterable.length) { return T.never; } else { return Ref.modify_(source, a => { if (a.length > 0) { return Tp.tuple(a[0], a.slice(1)); } throw new CS.IllegalArgumentException("No item in array"); }); } }), "pool", ({ get, iterable }) => makeFixed(M.fromEffect(get), iterable.length)), ({ pool }) => pool); } /** * Makes a new pool of the specified fixed size. The pool is returned in a * `Managed`, which governs the lifetime of the pool. When the pool is * shutdown because the `Managed` is used, the individual items allocated by * the pool will be released in some unspecified order. */ function makeFixed(get, min) { return makeWith(get, Tp.tuple(min, min), new STR.None()); } /** * Makes a new pool with the specified minimum and maximum sizes and time to * live before a pool whose excess items are not being used will be shrunk * down to the minimum size. The pool is returned in a `Managed`, which * governs the lifetime of the pool. When the pool is shutdown because the * `Managed` is used, the individual items allocated by the pool will be * released in some unspecified order. */ function make(get, range, timeToLive) { return makeWith(get, range, new STR.TimeToLive(timeToLive)); } /** * A more powerful variant of `make` that allows specifying a `Strategy` that * describes how a pool whose excess items are not being used will be shrunk * down to the minimum size. */ function makeWith(get, range, strategy) { return M.map_(M.tap_(M.bind_(M.bind_(M.let_(M.bind_(M.bind_(M.bind_(M.bind_(M.bind_(M.bind_(M.do, "env", () => M.environment()), "down", () => T.toManaged(Ref.makeRef(false))), "state", () => T.toManaged(Ref.makeRef({ size: 0, free: 0 }))), "items", () => T.toManaged(Q.makeBounded(Tp.get_(range, 1)))), "inv", () => T.toManaged(Ref.makeRef(HS.make()))), "initial", () => T.toManaged(strategy.initial())), "pool", ({ down, env, initial, inv, items, state }) => new DefaultPool(M.provideAll_(get, env), range, down, state, items, inv, strategy.track(initial))), "fiber", ({ pool }) => T.toManaged(T.forkDaemon(pool.initialize()))), "shrink", ({ initial, pool }) => T.toManaged(T.forkDaemon(strategy.run(initial, pool.excess(), pool.shrink())))), ({ fiber, pool, shrink }) => M.finalizer(T.zipRight_(T.zipRight_(F.interrupt(fiber), F.interrupt(shrink)), pool.shutdown()))), ({ pool }) => pool); } //# sourceMappingURL=Pool.js.map