UNPKG

@effect-ts/system

Version:

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

1,068 lines (948 loc) 27.7 kB
// ets_tracing: off import "../Operator/index.js" import * as AR from "../Collections/Immutable/Array/index.js" import * as Chunk from "../Collections/Immutable/Chunk/index.js" import * as Tp from "../Collections/Immutable/Tuple/index.js" import * as HS from "../Collections/Mutable/HashSet/index.js" import * as ES from "../Effect/ExecutionStrategy.js" import * as T from "../Effect/index.js" import * as Ex from "../Exit/index.js" import * as F from "../Fiber/index.js" import { pipe } from "../Function/index.js" import * as M from "../Managed/index.js" import * as RM from "../Managed/ReleaseMap/index.js" import * as P from "../Promise/index.js" import * as Q from "../Queue/index.js" import { XQueueInternal } from "../Queue/index.js" import * as Ref from "../Ref/index.js" import * as AB from "../Support/AtomicBoolean/index.js" import * as MQ from "../Support/MutableQueue/index.js" import type * as InternalHub from "./_internal/Hub.js" import * as HF from "./_internal/hubFactory.js" import * as U from "./_internal/unsafe.js" import * as PR from "./primitives.js" import * as S from "./Strategy.js" export type HubDequeue<R, E, A> = Q.XQueue<never, R, unknown, E, never, A> export type HubEnqueue<R, E, A> = Q.XQueue<R, never, E, unknown, A, never> export type Hub<A> = XHub<unknown, unknown, never, never, A, A> export const HubTypeId = Symbol() /** * A `Hub<RA, RB, EA, EB, A, B>` is an asynchronous message hub. Publishers * can publish messages of type `A` to the hub and subscribers can subscribe to * take messages of type `B` from the hub. Publishing messages can require an * environment of type `RA` and fail with an error of type `EA`. Taking * messages can require an environment of type `RB` and fail with an error of * type `EB`. */ export interface XHub<RA, RB, EA, EB, A, B> { readonly typeId: typeof HubTypeId readonly [PR._RA]: (_: RA) => void readonly [PR._RB]: (_: RB) => void readonly [PR._EA]: () => EA readonly [PR._EB]: () => EB readonly [PR._A]: (_: A) => void readonly [PR._B]: () => B } export abstract class XHubInternal<RA, RB, EA, EB, A, B> implements XHub<RA, RB, EA, EB, A, B> { readonly typeId: typeof HubTypeId = HubTypeId; readonly [PR._RA]!: (_: RA) => void; readonly [PR._RB]!: (_: RB) => void; readonly [PR._EA]!: () => EA; readonly [PR._EB]!: () => EB; readonly [PR._A]!: (_: A) => void; readonly [PR._B]!: () => B /** * Waits for the hub to be shut down. */ abstract awaitShutdown: T.UIO<void> /** * The maximum capacity of the hub. */ abstract capacity: number /** * Checks whether the hub is shut down. */ abstract isShutdown: T.UIO<boolean> /** * Publishes a message to the hub, returning whether the message was * published to the hub. */ abstract publish(a: A): T.Effect<RA, EA, boolean> /** * Publishes all of the specified messages to the hub, returning whether * they were published to the hub. */ abstract publishAll(as: Iterable<A>): T.Effect<RA, EA, boolean> /** * Shuts down the hub. */ abstract shutdown: T.UIO<void> /** * The current number of messages in the hub. */ abstract size: T.UIO<number> /** * Subscribes to receive messages from the hub. The resulting subscription * can be evaluated multiple times within the scope of the managed to take a * message from the hub each time. */ abstract subscribe: M.Managed<unknown, never, HubDequeue<RB, EB, B>> } /** * @ets_optimize remove */ export function concrete<RA, RB, EA, EB, A, B>( _: XHub<RA, RB, EA, EB, A, B> ): asserts _ is XHubInternal<RA, RB, EA, EB, A, B> { // } /** * Waits for the hub to be shut down. */ export function awaitShutdown<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B> ): T.UIO<void> { concrete(self) return self.awaitShutdown } /** * The maximum capacity of the hub. */ export function capacity<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B> ): number { concrete(self) return self.capacity } /** * Checks whether the hub is shut down. */ export function isShutdown<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B> ): T.UIO<boolean> { concrete(self) return self.isShutdown } /** * Publishes a message to the hub, returning whether the message was * published to the hub. */ export function publish_<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B>, a: A ): T.Effect<RA, EA, boolean> { concrete(self) return self.publish(a) } /** * Publishes a message to the hub, returning whether the message was * published to the hub. * * @ets_data_first publish_ */ export function publish<A>(a: A) { return <RA, RB, EA, EB, B>(self: XHub<RA, RB, EA, EB, A, B>) => publish_(self, a) } /** * Publishes all of the specified messages to the hub, returning whether * they were published to the hub. */ export function publishAll_<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B>, as: Iterable<A> ): T.Effect<RA, EA, boolean> { concrete(self) return self.publishAll(as) } /** * Publishes all of the specified messages to the hub, returning whether * they were published to the hub. * * @ets_data_first publishAll_ */ export function publishAll<A>(as: Iterable<A>) { return <RA, RB, EA, EB, B>(self: XHub<RA, RB, EA, EB, A, B>) => publishAll_(self, as) } /** * Shuts down the hub. */ export function shutdown<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B> ): T.UIO<void> { concrete(self) return self.shutdown } /** * The current number of messages in the hub. */ export function size<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B> ): T.UIO<number> { concrete(self) return self.size } /** * Subscribes to receive messages from the hub. The resulting subscription * can be evaluated multiple times within the scope of the managed to take a * message from the hub each time. */ export function subscribe<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B> ): M.Managed<unknown, never, HubDequeue<RB, EB, B>> { concrete(self) return self.subscribe } /** * Transforms messages published to the hub using the specified effectual * function. */ export function contramapM_<RA, RB, RC, EA, EB, EC, A, B, C>( self: XHub<RA, RB, EA, EB, A, B>, f: (c: C) => T.Effect<RC, EC, A> ): XHub<RC & RA, RB, EA | EC, EB, C, B> { return dimapM_(self, f, T.succeed) } /** * Transforms messages published to the hub using the specified effectual * function. * * @ets_data_first contramapM_ */ export function contramapM<RC, EC, A, C>(f: (c: C) => T.Effect<RC, EC, A>) { return <RA, RB, EA, EB, B>(self: XHub<RA, RB, EA, EB, A, B>) => contramapM_(self, f) } /** * Transforms messages published to and taken from the hub using the * specified functions. */ export function dimap_<RA, RB, EA, EB, A, B, C, D>( self: XHub<RA, RB, EA, EB, A, B>, f: (c: C) => A, g: (b: B) => D ): XHub<RA, RB, EA, EB, C, D> { return dimapM_( self, (c) => T.succeed(f(c)), (b) => T.succeed(g(b)) ) } /** * Transforms messages published to and taken from the hub using the * specified functions. * * @ets_data_first dimap_ */ export function dimap<A, B, C, D>(f: (c: C) => A, g: (b: B) => D) { return <RA, RB, EA, EB>(self: XHub<RA, RB, EA, EB, A, B>) => dimap_(self, f, g) } class DimapMImplementation< RA, RB, RC, RD, EA, EB, EC, ED, A, B, C, D > extends XHubInternal<RC & RA, RD & RB, EA | EC, EB | ED, C, D> { awaitShutdown: T.UIO<void> capacity: number isShutdown: T.UIO<boolean> shutdown: T.UIO<void> size: T.UIO<number> subscribe: M.Managed<unknown, never, HubDequeue<RD & RB, ED | EB, D>> constructor( readonly source: XHubInternal<RA, RB, EA, EB, A, B>, readonly f: (c: C) => T.Effect<RC, EC, A>, g: (b: B) => T.Effect<RD, ED, D> ) { super() this.awaitShutdown = source.awaitShutdown this.capacity = source.capacity this.isShutdown = source.isShutdown this.shutdown = source.shutdown this.size = source.size this.subscribe = M.map_(source.subscribe, Q.mapM(g)) } publish(c: C) { return T.chain_(this.f(c), (a) => this.source.publish(a)) } publishAll(cs: Iterable<C>) { return T.chain_(T.forEach_(cs, this.f), (as) => this.source.publishAll(as)) } } /** * Transforms messages published to and taken from the hub using the * specified effectual functions. */ export function dimapM_<RA, RB, RC, RD, EA, EB, EC, ED, A, B, C, D>( self: XHub<RA, RB, EA, EB, A, B>, f: (c: C) => T.Effect<RC, EC, A>, g: (b: B) => T.Effect<RD, ED, D> ): XHub<RC & RA, RD & RB, EA | EC, EB | ED, C, D> { concrete(self) return new DimapMImplementation(self, f, g) } /** * Transforms messages published to and taken from the hub using the * specified effectual functions. * * @ets_data_first dimapM_ */ export function dimapM<A, B, C, D, EC, ED, RC, RD>( f: (c: C) => T.Effect<RC, EC, A>, g: (b: B) => T.Effect<RD, ED, D> ) { return <RA, RB, EA, EB>(self: XHub<RA, RB, EA, EB, A, B>) => dimapM_(self, f, g) } class filterInputMImplementation<RA, RA1, RB, EA, EA1, EB, A, B> extends XHubInternal< RA & RA1, RB, EA | EA1, EB, A, B > { awaitShutdown: T.UIO<void> capacity: number isShutdown: T.UIO<boolean> shutdown: T.UIO<void> size: T.UIO<number> subscribe: M.Managed<unknown, never, HubDequeue<RB, EB, B>> constructor( readonly source: XHubInternal<RA, RB, EA, EB, A, B>, readonly f: (a: A) => T.Effect<RA1, EA1, boolean> ) { super() this.awaitShutdown = source.awaitShutdown this.capacity = source.capacity this.isShutdown = source.isShutdown this.shutdown = source.shutdown this.size = source.size this.subscribe = source.subscribe } publish(a: A) { return T.chain_(this.f(a), (b) => (b ? this.source.publish(a) : T.succeed(false))) } publishAll(as: Iterable<A>) { return T.chain_(T.filter_(as, this.f), (as) => AR.isNonEmpty(as) ? this.source.publishAll(as) : T.succeed(false) ) } } /** * Filters messages published to the hub using the specified function. */ export function filterInput_<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B>, f: (a: A) => boolean ) { return filterInputM_(self, (a) => T.succeed(f(a))) } /** * Filters messages published to the hub using the specified function. * * @ets_data_first filterInput_ */ export function filterInput<A>(f: (a: A) => boolean) { return <RA, RB, EA, EB, B>(self: XHub<RA, RB, EA, EB, A, B>) => filterInput_(self, f) } /** * Filters messages published to the hub using the specified effectual * function. */ export function filterInputM_<RA, RA1, RB, EA, EA1, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B>, f: (a: A) => T.Effect<RA1, EA1, boolean> ): XHub<RA & RA1, RB, EA | EA1, EB, A, B> { concrete(self) return new filterInputMImplementation(self, f) } /** * Filters messages published to the hub using the specified effectual * function. * * @ets_data_first filterInputM_ */ export function filterInputM<RA1, EA1, A>(f: (a: A) => T.Effect<RA1, EA1, boolean>) { return <RA, RB, EA, EB, B>(self: XHub<RA, RB, EA, EB, A, B>) => filterInputM_(self, f) } /** * Filters messages taken from the hub using the specified function. */ export function filterOutput_<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B>, f: (b: B) => boolean ): XHub<RA, RB, EA, EB, A, B> { return filterOutputM_(self, (b) => T.succeed(f(b))) } /** * Filters messages taken from the hub using the specified function. * * @ets_data_first filterOutput_ */ export function filterOutput<B>(f: (b: B) => boolean) { return <RA, RB, EA, EB, A>(self: XHub<RA, RB, EA, EB, A, B>) => filterOutput_(self, f) } class filterOutputMImplementation<RA, RB, RB1, EA, EB, EB1, A, B> extends XHubInternal< RA, RB & RB1, EA, EB | EB1, A, B > { awaitShutdown: T.UIO<void> capacity: number isShutdown: T.UIO<boolean> shutdown: T.UIO<void> size: T.UIO<number> subscribe: M.Managed<unknown, never, HubDequeue<RB & RB1, EB | EB1, B>> constructor( readonly source: XHubInternal<RA, RB, EA, EB, A, B>, readonly f: (b: B) => T.Effect<RB1, EB1, boolean> ) { super() this.awaitShutdown = source.awaitShutdown this.capacity = source.capacity this.isShutdown = source.isShutdown this.shutdown = source.shutdown this.size = source.size this.subscribe = M.map_(source.subscribe, Q.filterOutputM(f)) } publish(a: A) { return this.source.publish(a) } publishAll(as: Iterable<A>) { return this.source.publishAll(as) } } /** * Filters messages taken from the hub using the specified effectual * function. */ export function filterOutputM_<RA, RB, RB1, EA, EB, EB1, A, B>( self: XHub<RA, RB, EA, EB, A, B>, f: (a: B) => T.Effect<RB1, EB1, boolean> ): XHub<RA, RB & RB1, EA, EB | EB1, A, B> { concrete(self) return new filterOutputMImplementation(self, f) } /** * Filters messages taken from the hub using the specified effectual * function. * * @ets_data_first filterOutputM_ */ export function filterOutputM<RB1, EB1, B>(f: (a: B) => T.Effect<RB1, EB1, boolean>) { return <RA, RB, EA, EB, A>(self: XHub<RA, RB, EA, EB, A, B>) => filterOutputM_(self, f) } /** * Transforms messages taken from the hub using the specified function. */ export function map_<RA, RB, EA, EB, A, B, C>( self: XHub<RA, RB, EA, EB, A, B>, f: (b: B) => C ): XHub<RA, RB, EA, EB, A, C> { return mapM_(self, (b) => T.succeed(f(b))) } /** * Transforms messages taken from the hub using the specified function. * * @ets_data_first map_ */ export function map<B, C>(f: (b: B) => C) { return <RA, RB, EA, EB, A>(self: XHub<RA, RB, EA, EB, A, B>) => map_(self, f) } /** * Transforms messages taken from the hub using the specified effectual * function. */ export function mapM_<RA, RB, RC, EA, EB, EC, A, B, C>( self: XHub<RA, RB, EA, EB, A, B>, f: (b: B) => T.Effect<RC, EC, C> ): XHub<RA, RC & RB, EA, EB | EC, A, C> { return dimapM_(self, (a) => T.succeed<A>(a), f) } /** * Transforms messages taken from the hub using the specified effectual * function. * * @ets_data_first mapM_ */ export function mapM<B, C, EC, RC>(f: (b: B) => T.Effect<RC, EC, C>) { return <A, EA, EB, RA, RB>(self: XHub<RA, RB, EA, EB, A, B>) => mapM_(self, f) } class ToQueueImplementation<RA, RB, EA, EB, A, B> extends XQueueInternal< RA, never, EA, unknown, A, never > { awaitShutdown: T.UIO<void> capacity: number isShutdown: T.UIO<boolean> shutdown: T.UIO<void> size: T.UIO<number> take: T.Effect<unknown, never, never> takeAll: T.Effect<unknown, never, Chunk.Chunk<never>> constructor(readonly source: XHubInternal<RA, RB, EA, EB, A, B>) { super() this.awaitShutdown = source.awaitShutdown this.capacity = source.capacity this.isShutdown = source.isShutdown this.shutdown = source.shutdown this.size = source.size this.take = T.never this.takeAll = T.succeed(Chunk.empty()) } offer(a: A): T.Effect<RA, EA, boolean> { return this.source.publish(a) } offerAll(as: Iterable<A>): T.Effect<RA, EA, boolean> { return this.source.publishAll(as) } takeUpTo(): T.Effect<unknown, never, Chunk.Chunk<never>> { return T.succeed(Chunk.empty()) } } /** * Views the hub as a queue that can only be written to. */ export function toQueue<RA, RB, EA, EB, A, B>( self: XHub<RA, RB, EA, EB, A, B> ): HubEnqueue<RA, EA, A> { concrete(self) return new ToQueueImplementation(self) } /** * Creates a bounded hub with the back pressure strategy. The hub will retain * messages until they have been taken by all subscribers, applying back * pressure to publishers if the hub is at capacity. * * For best performance use capacities that are powers of two. */ export function makeBounded<A>(requestedCapacity: number): T.UIO<Hub<A>> { return T.chain_( T.succeedWith(() => HF.makeBounded<A>(requestedCapacity)), (_) => makeHub(_, new S.BackPressure()) ) } /** * Creates a bounded hub with the back pressure strategy. The hub will retain * messages until they have been taken by all subscribers, applying back * pressure to publishers if the hub is at capacity. * * For best performance use capacities that are powers of two. */ export function unsafeMakeBounded<A>(requestedCapacity: number): Hub<A> { const releaseMap = new RM.ReleaseMap( Ref.unsafeMakeRef<RM.State>(new RM.Running(0, new Map())) ) return unsafeMakeHub( HF.makeBounded<A>(requestedCapacity), makeSubscribersHashSet<A>(), releaseMap, P.unsafeMake<never, void>(F.None), new AB.AtomicBoolean(false), new S.BackPressure() ) } /** * Creates a bounded hub with the dropping strategy. The hub will drop new * messages if the hub is at capacity. * * For best performance use capacities that are powers of two. */ export function makeDropping<A>(requestedCapacity: number): T.UIO<Hub<A>> { return T.chain_( T.succeedWith(() => { return HF.makeBounded<A>(requestedCapacity) }), (_) => makeHub(_, new S.Dropping()) ) } /** * Creates a bounded hub with the dropping strategy. The hub will drop new * messages if the hub is at capacity. * * For best performance use capacities that are powers of two. */ export function unsafeMakeDropping<A>(requestedCapacity: number): Hub<A> { const releaseMap = new RM.ReleaseMap( Ref.unsafeMakeRef<RM.State>(new RM.Running(0, new Map())) ) return unsafeMakeHub( HF.makeBounded<A>(requestedCapacity), makeSubscribersHashSet<A>(), releaseMap, P.unsafeMake<never, void>(F.None), new AB.AtomicBoolean(false), new S.Dropping() ) } /** * Creates a bounded hub with the sliding strategy. The hub will add new * messages and drop old messages if the hub is at capacity. * * For best performance use capacities that are powers of two. */ export function makeSliding<A>(requestedCapacity: number): T.UIO<Hub<A>> { return T.chain_( T.succeedWith(() => { return HF.makeBounded<A>(requestedCapacity) }), (_) => makeHub(_, new S.Sliding()) ) } /** * Creates a bounded hub with the sliding strategy. The hub will add new * messages and drop old messages if the hub is at capacity. * * For best performance use capacities that are powers of two. */ export function unsafeMakeSliding<A>(requestedCapacity: number): Hub<A> { const releaseMap = new RM.ReleaseMap( Ref.unsafeMakeRef<RM.State>(new RM.Running(0, new Map())) ) return unsafeMakeHub( HF.makeBounded<A>(requestedCapacity), makeSubscribersHashSet<A>(), releaseMap, P.unsafeMake<never, void>(F.None), new AB.AtomicBoolean(false), new S.Sliding() ) } /** * Creates an unbounded hub. */ export function makeUnbounded<A>(): T.UIO<Hub<A>> { return T.chain_( T.succeedWith(() => { return HF.makeUnbounded<A>() }), (_) => makeHub(_, new S.Dropping()) ) } /** * Creates an unbounded hub. */ export function unsafeMakeUnbounded<A>(): Hub<A> { const releaseMap = new RM.ReleaseMap( Ref.unsafeMakeRef<RM.State>(new RM.Running(0, new Map())) ) return unsafeMakeHub( HF.makeUnbounded<A>(), makeSubscribersHashSet<A>(), releaseMap, P.unsafeMake<never, void>(F.None), new AB.AtomicBoolean(false), new S.Dropping() ) } class UnsafeMakeHubImplementation<A> extends XHubInternal< unknown, unknown, never, never, A, A > { awaitShutdown: T.UIO<void> capacity: number isShutdown: T.UIO<boolean> shutdown: T.UIO<void> size: T.UIO<number> subscribe: M.Managed<unknown, never, HubDequeue<unknown, never, A>> constructor( private hub: InternalHub.Hub<A>, private subscribers: HS.HashSet< Tp.Tuple<[InternalHub.Subscription<A>, MQ.MutableQueue<P.Promise<never, A>>]> >, releaseMap: RM.ReleaseMap, shutdownHook: P.Promise<never, void>, private shutdownFlag: AB.AtomicBoolean, private strategy: S.Strategy<A> ) { super() this.awaitShutdown = P.await(shutdownHook) this.capacity = hub.capacity this.isShutdown = T.succeedWith(() => shutdownFlag.get) this.shutdown = T.uninterruptible( T.suspend((_, fiberId) => { shutdownFlag.set(true) return T.asUnit( T.whenM_( T.zipRight_( RM.releaseAll(Ex.interrupt(fiberId), ES.parallel)(releaseMap), strategy.shutdown ), P.succeed_(shutdownHook, undefined) ) ) }) ) this.size = T.suspend(() => { if (shutdownFlag.get) { return T.interrupt } return T.succeed(hub.size()) }) this.subscribe = pipe( M.do, M.bind("dequeue", () => T.toManaged(makeSubscription(hub, subscribers, strategy)) ), M.tap(({ dequeue }) => M.makeExit_(RM.add((_) => Q.shutdown(dequeue))(releaseMap), (finalizer, exit) => finalizer(exit) ) ), M.map(({ dequeue }) => dequeue) ) } publish(a: A): T.Effect<unknown, never, boolean> { return T.suspend(() => { if (this.shutdownFlag.get) { return T.interrupt } if (this.hub.publish(a)) { this.strategy.unsafeCompleteSubscribers(this.hub, this.subscribers) return T.succeed(true) } return this.strategy.handleSurplus( this.hub, this.subscribers, Chunk.single(a), this.shutdownFlag ) }) } publishAll(as: Iterable<A>): T.Effect<unknown, never, boolean> { return T.suspend(() => { if (this.shutdownFlag.get) { return T.interrupt } const surplus = U.unsafePublishAll(this.hub, as) this.strategy.unsafeCompleteSubscribers(this.hub, this.subscribers) if (Chunk.isEmpty(surplus)) { return T.succeed(true) } return this.strategy.handleSurplus( this.hub, this.subscribers, surplus, this.shutdownFlag ) }) } } function makeHub<A>(hub: InternalHub.Hub<A>, strategy: S.Strategy<A>): T.UIO<Hub<A>> { return T.chain_(RM.makeReleaseMap, (releaseMap) => { return T.map_(P.make<never, void>(), (promise) => { return unsafeMakeHub( hub, makeSubscribersHashSet<A>(), releaseMap, promise, new AB.AtomicBoolean(false), strategy ) }) }) } /** * Unsafely creates a hub with the specified strategy. */ function unsafeMakeHub<A>( hub: InternalHub.Hub<A>, subscribers: HS.HashSet< Tp.Tuple<[InternalHub.Subscription<A>, MQ.MutableQueue<P.Promise<never, A>>]> >, releaseMap: RM.ReleaseMap, shutdownHook: P.Promise<never, void>, shutdownFlag: AB.AtomicBoolean, strategy: S.Strategy<A> ): Hub<A> { return new UnsafeMakeHubImplementation( hub, subscribers, releaseMap, shutdownHook, shutdownFlag, strategy ) } /** * Creates a subscription with the specified strategy. */ function makeSubscription<A>( hub: InternalHub.Hub<A>, subscribers: HS.HashSet< Tp.Tuple<[InternalHub.Subscription<A>, MQ.MutableQueue<P.Promise<never, A>>]> >, strategy: S.Strategy<A> ): T.UIO<Q.Dequeue<A>> { return T.map_(P.make<never, void>(), (promise) => { return unsafeMakeSubscription( hub, subscribers, hub.subscribe(), new MQ.Unbounded<P.Promise<never, A>>(), promise, new AB.AtomicBoolean(false), strategy ) }) } class UnsafeMakeSubscriptionImplementation<A> extends XQueueInternal< never, unknown, unknown, never, never, A > { constructor( private hub: InternalHub.Hub<A>, private subscribers: HS.HashSet< Tp.Tuple<[InternalHub.Subscription<A>, MQ.MutableQueue<P.Promise<never, A>>]> >, private subscription: InternalHub.Subscription<A>, private pollers: MQ.MutableQueue<P.Promise<never, A>>, private shutdownHook: P.Promise<never, void>, private shutdownFlag: AB.AtomicBoolean, private strategy: S.Strategy<A> ) { super() } awaitShutdown: T.UIO<void> = P.await(this.shutdownHook) capacity: number = this.hub.capacity isShutdown: T.UIO<boolean> = T.succeedWith(() => this.shutdownFlag.get) shutdown: T.UIO<void> = T.uninterruptible( T.suspend((_, fiberId) => { this.shutdownFlag.set(true) return T.asUnit( T.whenM_( T.zipRight_( T.forEachPar_(U.unsafePollAllQueue(this.pollers), (_) => { return P.interruptAs(fiberId)(_) }), T.succeedWith(() => this.subscription.unsubscribe()) ), P.succeed_(this.shutdownHook, undefined) ) ) }) ) size: T.UIO<number> = T.suspend(() => { if (this.shutdownFlag.get) { return T.interrupt } return T.succeed(this.subscription.size()) }) offer(_: never): T.Effect<never, unknown, boolean> { return T.succeed(false) } offerAll(_: Iterable<never>): T.Effect<never, unknown, boolean> { return T.succeed(false) } take: T.Effect<unknown, never, A> = T.suspend((_, fiberId) => { if (this.shutdownFlag.get) { return T.interrupt } const message = this.pollers.isEmpty ? this.subscription.poll(MQ.EmptyQueue) : MQ.EmptyQueue if (message === MQ.EmptyQueue) { const promise = P.unsafeMake<never, A>(fiberId) return T.onInterrupt_( T.suspend(() => { this.pollers.offer(promise) this.subscribers.add(Tp.tuple(this.subscription, this.pollers)) this.strategy.unsafeCompletePollers( this.hub, this.subscribers, this.subscription, this.pollers ) if (this.shutdownFlag.get) { return T.interrupt } else { return P.await(promise) } }), () => T.succeedWith(() => { U.unsafeRemove(this.pollers, promise) }) ) } else { this.strategy.unsafeOnHubEmptySpace(this.hub, this.subscribers) return T.succeed(message) } }) takeAll: T.Effect<unknown, never, Chunk.Chunk<A>> = T.suspend(() => { if (this.shutdownFlag.get) { return T.interrupt } const as = this.pollers.isEmpty ? U.unsafePollAllSubscription(this.subscription) : Chunk.empty<A>() this.strategy.unsafeOnHubEmptySpace(this.hub, this.subscribers) return T.succeed(as) }) takeUpTo(n: number): T.Effect<unknown, never, Chunk.Chunk<A>> { return T.suspend(() => { if (this.shutdownFlag.get) { return T.interrupt } const as = this.pollers.isEmpty ? U.unsafePollN(this.subscription, n) : Chunk.empty<A>() this.strategy.unsafeOnHubEmptySpace(this.hub, this.subscribers) return T.succeed(as) }) } } /** * Unsafely creates a subscription with the specified strategy. */ function unsafeMakeSubscription<A>( hub: InternalHub.Hub<A>, subscribers: HS.HashSet< Tp.Tuple<[InternalHub.Subscription<A>, MQ.MutableQueue<P.Promise<never, A>>]> >, subscription: InternalHub.Subscription<A>, pollers: MQ.MutableQueue<P.Promise<never, A>>, shutdownHook: P.Promise<never, void>, shutdownFlag: AB.AtomicBoolean, strategy: S.Strategy<A> ): Q.Dequeue<A> { return new UnsafeMakeSubscriptionImplementation( hub, subscribers, subscription, pollers, shutdownHook, shutdownFlag, strategy ) } function makeSubscribersHashSet<A>(): HS.HashSet< Tp.Tuple<[InternalHub.Subscription<A>, MQ.MutableQueue<P.Promise<never, A>>]> > { return HS.make< Tp.Tuple<[InternalHub.Subscription<A>, MQ.MutableQueue<P.Promise<never, A>>]> >() }