@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
JavaScript
"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