@clayroach/effect 3.19.14-source-capture.8 → 3.19.14-source-trace.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SourceLocation/package.json +6 -0
- package/dist/cjs/Effect.js +2 -28
- package/dist/cjs/Effect.js.map +1 -1
- package/dist/cjs/FiberRef.js +12 -1
- package/dist/cjs/FiberRef.js.map +1 -1
- package/dist/cjs/Layer.js +2 -24
- package/dist/cjs/Layer.js.map +1 -1
- package/dist/cjs/RuntimeFlags.js +1 -29
- package/dist/cjs/RuntimeFlags.js.map +1 -1
- package/dist/cjs/SourceLocation.js +60 -0
- package/dist/cjs/SourceLocation.js.map +1 -0
- package/dist/cjs/Tracer.js +1 -15
- package/dist/cjs/Tracer.js.map +1 -1
- package/dist/cjs/Utils.js +1 -1
- package/dist/cjs/Utils.js.map +1 -1
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/clock.js +1 -1
- package/dist/cjs/internal/clock.js.map +1 -1
- package/dist/cjs/internal/core.js +17 -50
- package/dist/cjs/internal/core.js.map +1 -1
- package/dist/cjs/internal/effect/circular.js +18 -30
- package/dist/cjs/internal/effect/circular.js.map +1 -1
- package/dist/cjs/internal/fiberRuntime.js +16 -65
- package/dist/cjs/internal/fiberRuntime.js.map +1 -1
- package/dist/cjs/internal/layer/circular.js +1 -5
- package/dist/cjs/internal/layer/circular.js.map +1 -1
- package/dist/cjs/internal/layer.js +1 -3
- package/dist/cjs/internal/layer.js.map +1 -1
- package/dist/cjs/internal/logger.js +25 -2
- package/dist/cjs/internal/logger.js.map +1 -1
- package/dist/cjs/internal/runtimeFlags.js +2 -11
- package/dist/cjs/internal/runtimeFlags.js.map +1 -1
- package/dist/cjs/internal/tracer.js +1 -114
- package/dist/cjs/internal/tracer.js.map +1 -1
- package/dist/dts/Config.d.ts +2 -2
- package/dist/dts/Config.d.ts.map +1 -1
- package/dist/dts/Effect.d.ts +8 -29
- package/dist/dts/Effect.d.ts.map +1 -1
- package/dist/dts/FiberRef.d.ts +12 -0
- package/dist/dts/FiberRef.d.ts.map +1 -1
- package/dist/dts/Layer.d.ts +0 -22
- package/dist/dts/Layer.d.ts.map +1 -1
- package/dist/dts/RuntimeFlags.d.ts +0 -28
- package/dist/dts/RuntimeFlags.d.ts.map +1 -1
- package/dist/dts/SourceLocation.d.ts +88 -0
- package/dist/dts/SourceLocation.d.ts.map +1 -0
- package/dist/dts/Tracer.d.ts +0 -15
- package/dist/dts/Tracer.d.ts.map +1 -1
- package/dist/dts/index.d.ts +6 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/core.d.ts.map +1 -1
- package/dist/dts/internal/layer.d.ts.map +1 -1
- package/dist/dts/internal/runtimeFlags.d.ts.map +1 -1
- package/dist/esm/Effect.js +0 -26
- package/dist/esm/Effect.js.map +1 -1
- package/dist/esm/FiberRef.js +11 -0
- package/dist/esm/FiberRef.js.map +1 -1
- package/dist/esm/Layer.js +0 -22
- package/dist/esm/Layer.js.map +1 -1
- package/dist/esm/RuntimeFlags.js +0 -28
- package/dist/esm/RuntimeFlags.js.map +1 -1
- package/dist/esm/SourceLocation.js +51 -0
- package/dist/esm/SourceLocation.js.map +1 -0
- package/dist/esm/Tracer.js +0 -14
- package/dist/esm/Tracer.js.map +1 -1
- package/dist/esm/Utils.js +1 -1
- package/dist/esm/Utils.js.map +1 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/clock.js +1 -1
- package/dist/esm/internal/clock.js.map +1 -1
- package/dist/esm/internal/core.js +12 -45
- package/dist/esm/internal/core.js.map +1 -1
- package/dist/esm/internal/effect/circular.js +18 -30
- package/dist/esm/internal/effect/circular.js.map +1 -1
- package/dist/esm/internal/fiberRuntime.js +13 -60
- package/dist/esm/internal/fiberRuntime.js.map +1 -1
- package/dist/esm/internal/layer/circular.js +0 -4
- package/dist/esm/internal/layer/circular.js.map +1 -1
- package/dist/esm/internal/layer.js +0 -2
- package/dist/esm/internal/layer.js.map +1 -1
- package/dist/esm/internal/logger.js +25 -2
- package/dist/esm/internal/logger.js.map +1 -1
- package/dist/esm/internal/runtimeFlags.js +1 -9
- package/dist/esm/internal/runtimeFlags.js.map +1 -1
- package/dist/esm/internal/tracer.js +0 -111
- package/dist/esm/internal/tracer.js.map +1 -1
- package/package.json +12 -1
- package/src/Arbitrary.ts +1101 -0
- package/src/Array.ts +3589 -0
- package/src/BigDecimal.ts +1349 -0
- package/src/BigInt.ts +643 -0
- package/src/Boolean.ts +287 -0
- package/src/Brand.ts +360 -0
- package/src/Cache.ts +281 -0
- package/src/Cause.ts +1555 -0
- package/src/Channel.ts +2355 -0
- package/src/ChildExecutorDecision.ts +146 -0
- package/src/Chunk.ts +1495 -0
- package/src/Clock.ts +111 -0
- package/src/Config.ts +542 -0
- package/src/ConfigError.ts +270 -0
- package/src/ConfigProvider.ts +333 -0
- package/src/ConfigProviderPathPatch.ts +100 -0
- package/src/Console.ts +226 -0
- package/src/Context.ts +585 -0
- package/src/Cron.ts +706 -0
- package/src/Data.ts +596 -0
- package/src/DateTime.ts +1686 -0
- package/src/DefaultServices.ts +34 -0
- package/src/Deferred.ts +301 -0
- package/src/Differ.ts +450 -0
- package/src/Duration.ts +1000 -0
- package/src/Effect.ts +14817 -0
- package/src/Effectable.ts +107 -0
- package/src/Either.ts +1040 -0
- package/src/Encoding.ts +195 -0
- package/src/Equal.ts +98 -0
- package/src/Equivalence.ts +235 -0
- package/src/ExecutionPlan.ts +308 -0
- package/src/ExecutionStrategy.ts +119 -0
- package/src/Exit.ts +467 -0
- package/src/FastCheck.ts +9 -0
- package/src/Fiber.ts +744 -0
- package/src/FiberHandle.ts +540 -0
- package/src/FiberId.ts +195 -0
- package/src/FiberMap.ts +656 -0
- package/src/FiberRef.ts +444 -0
- package/src/FiberRefs.ts +204 -0
- package/src/FiberRefsPatch.ts +105 -0
- package/src/FiberSet.ts +491 -0
- package/src/FiberStatus.ts +108 -0
- package/src/Function.ts +1222 -0
- package/src/GlobalValue.ts +53 -0
- package/src/Graph.ts +3732 -0
- package/src/GroupBy.ts +103 -0
- package/src/HKT.ts +45 -0
- package/src/Hash.ts +195 -0
- package/src/HashMap.ts +519 -0
- package/src/HashRing.ts +317 -0
- package/src/HashSet.ts +2346 -0
- package/src/Inspectable.ts +287 -0
- package/src/Iterable.ts +1119 -0
- package/src/JSONSchema.ts +1044 -0
- package/src/KeyedPool.ts +167 -0
- package/src/Layer.ts +1228 -0
- package/src/LayerMap.ts +436 -0
- package/src/List.ts +977 -0
- package/src/LogLevel.ts +285 -0
- package/src/LogSpan.ts +25 -0
- package/src/Logger.ts +702 -0
- package/src/Mailbox.ts +268 -0
- package/src/ManagedRuntime.ts +180 -0
- package/src/Match.ts +1477 -0
- package/src/MergeDecision.ts +95 -0
- package/src/MergeState.ts +172 -0
- package/src/MergeStrategy.ts +107 -0
- package/src/Metric.ts +780 -0
- package/src/MetricBoundaries.ts +69 -0
- package/src/MetricHook.ts +151 -0
- package/src/MetricKey.ts +224 -0
- package/src/MetricKeyType.ts +262 -0
- package/src/MetricLabel.ts +47 -0
- package/src/MetricPair.ts +71 -0
- package/src/MetricPolling.ts +148 -0
- package/src/MetricRegistry.ts +48 -0
- package/src/MetricState.ts +257 -0
- package/src/Micro.ts +4405 -0
- package/src/ModuleVersion.ts +18 -0
- package/src/MutableHashMap.ts +411 -0
- package/src/MutableHashSet.ts +706 -0
- package/src/MutableList.ts +297 -0
- package/src/MutableQueue.ts +227 -0
- package/src/MutableRef.ts +202 -0
- package/src/NonEmptyIterable.ts +32 -0
- package/src/Number.ts +1071 -0
- package/src/Option.ts +2170 -0
- package/src/Order.ts +373 -0
- package/src/Ordering.ts +111 -0
- package/src/ParseResult.ts +2031 -0
- package/src/PartitionedSemaphore.ts +200 -0
- package/src/Pipeable.ts +566 -0
- package/src/Pool.ts +204 -0
- package/src/Predicate.ts +1405 -0
- package/src/Pretty.ts +205 -0
- package/src/PrimaryKey.ts +23 -0
- package/src/PubSub.ts +182 -0
- package/src/Queue.ts +644 -0
- package/src/Random.ts +204 -0
- package/src/RateLimiter.ts +138 -0
- package/src/RcMap.ts +141 -0
- package/src/RcRef.ts +122 -0
- package/src/Readable.ts +93 -0
- package/src/Record.ts +1274 -0
- package/src/RedBlackTree.ts +421 -0
- package/src/Redacted.ts +144 -0
- package/src/Ref.ts +180 -0
- package/src/RegExp.ts +38 -0
- package/src/Reloadable.ts +127 -0
- package/src/Request.ts +347 -0
- package/src/RequestBlock.ts +118 -0
- package/src/RequestResolver.ts +366 -0
- package/src/Resource.ts +119 -0
- package/src/Runtime.ts +383 -0
- package/src/RuntimeFlags.ts +336 -0
- package/src/RuntimeFlagsPatch.ts +183 -0
- package/src/STM.ts +2045 -0
- package/src/Schedule.ts +2219 -0
- package/src/ScheduleDecision.ts +62 -0
- package/src/ScheduleInterval.ts +151 -0
- package/src/ScheduleIntervals.ts +122 -0
- package/src/Scheduler.ts +353 -0
- package/src/Schema.ts +10914 -0
- package/src/SchemaAST.ts +3043 -0
- package/src/Scope.ts +204 -0
- package/src/ScopedCache.ts +151 -0
- package/src/ScopedRef.ts +117 -0
- package/src/Secret.ts +88 -0
- package/src/SingleProducerAsyncInput.ts +67 -0
- package/src/Sink.ts +1461 -0
- package/src/SortedMap.ts +287 -0
- package/src/SortedSet.ts +390 -0
- package/src/SourceLocation.ts +108 -0
- package/src/Stream.ts +6468 -0
- package/src/StreamEmit.ts +136 -0
- package/src/StreamHaltStrategy.ts +123 -0
- package/src/Streamable.ts +45 -0
- package/src/String.ts +778 -0
- package/src/Struct.ts +243 -0
- package/src/Subscribable.ts +100 -0
- package/src/SubscriptionRef.ts +298 -0
- package/src/Supervisor.ts +240 -0
- package/src/Symbol.ts +29 -0
- package/src/SynchronizedRef.ts +270 -0
- package/src/TArray.ts +495 -0
- package/src/TDeferred.ts +100 -0
- package/src/TMap.ts +515 -0
- package/src/TPriorityQueue.ts +223 -0
- package/src/TPubSub.ts +200 -0
- package/src/TQueue.ts +432 -0
- package/src/TRandom.ts +129 -0
- package/src/TReentrantLock.ts +224 -0
- package/src/TRef.ts +178 -0
- package/src/TSemaphore.ts +129 -0
- package/src/TSet.ts +365 -0
- package/src/TSubscriptionRef.ts +192 -0
- package/src/Take.ts +258 -0
- package/src/TestAnnotation.ts +158 -0
- package/src/TestAnnotationMap.ts +119 -0
- package/src/TestAnnotations.ts +117 -0
- package/src/TestClock.ts +556 -0
- package/src/TestConfig.ts +47 -0
- package/src/TestContext.ts +36 -0
- package/src/TestLive.ts +53 -0
- package/src/TestServices.ts +390 -0
- package/src/TestSized.ts +55 -0
- package/src/Tracer.ts +182 -0
- package/src/Trie.ts +840 -0
- package/src/Tuple.ts +305 -0
- package/src/Types.ts +353 -0
- package/src/Unify.ts +113 -0
- package/src/UpstreamPullRequest.ts +117 -0
- package/src/UpstreamPullStrategy.ts +121 -0
- package/src/Utils.ts +809 -0
- package/src/index.ts +1568 -0
- package/src/internal/array.ts +8 -0
- package/src/internal/blockedRequests.ts +520 -0
- package/src/internal/cache.ts +733 -0
- package/src/internal/cause.ts +1050 -0
- package/src/internal/channel/channelExecutor.ts +1200 -0
- package/src/internal/channel/channelState.ts +134 -0
- package/src/internal/channel/childExecutorDecision.ts +96 -0
- package/src/internal/channel/continuation.ts +200 -0
- package/src/internal/channel/mergeDecision.ts +113 -0
- package/src/internal/channel/mergeState.ts +120 -0
- package/src/internal/channel/mergeStrategy.ts +72 -0
- package/src/internal/channel/singleProducerAsyncInput.ts +259 -0
- package/src/internal/channel/subexecutor.ts +229 -0
- package/src/internal/channel/upstreamPullRequest.ts +84 -0
- package/src/internal/channel/upstreamPullStrategy.ts +87 -0
- package/src/internal/channel.ts +2603 -0
- package/src/internal/clock.ts +95 -0
- package/src/internal/completedRequestMap.ts +9 -0
- package/src/internal/concurrency.ts +54 -0
- package/src/internal/config.ts +716 -0
- package/src/internal/configError.ts +304 -0
- package/src/internal/configProvider/pathPatch.ts +97 -0
- package/src/internal/configProvider.ts +799 -0
- package/src/internal/console.ts +153 -0
- package/src/internal/context.ts +337 -0
- package/src/internal/core-effect.ts +2293 -0
- package/src/internal/core-stream.ts +998 -0
- package/src/internal/core.ts +3189 -0
- package/src/internal/data.ts +36 -0
- package/src/internal/dataSource.ts +327 -0
- package/src/internal/dateTime.ts +1277 -0
- package/src/internal/defaultServices/console.ts +100 -0
- package/src/internal/defaultServices.ts +163 -0
- package/src/internal/deferred.ts +46 -0
- package/src/internal/differ/chunkPatch.ts +211 -0
- package/src/internal/differ/contextPatch.ts +232 -0
- package/src/internal/differ/hashMapPatch.ts +220 -0
- package/src/internal/differ/hashSetPatch.ts +176 -0
- package/src/internal/differ/orPatch.ts +311 -0
- package/src/internal/differ/readonlyArrayPatch.ts +210 -0
- package/src/internal/differ.ts +200 -0
- package/src/internal/doNotation.ts +80 -0
- package/src/internal/effect/circular.ts +895 -0
- package/src/internal/effectable.ts +131 -0
- package/src/internal/either.ts +110 -0
- package/src/internal/encoding/base64.ts +286 -0
- package/src/internal/encoding/base64Url.ts +29 -0
- package/src/internal/encoding/common.ts +51 -0
- package/src/internal/encoding/hex.ts +315 -0
- package/src/internal/errors.ts +7 -0
- package/src/internal/executionPlan.ts +114 -0
- package/src/internal/executionStrategy.ts +74 -0
- package/src/internal/fiber.ts +388 -0
- package/src/internal/fiberId.ts +267 -0
- package/src/internal/fiberMessage.ts +82 -0
- package/src/internal/fiberRefs/patch.ts +144 -0
- package/src/internal/fiberRefs.ts +297 -0
- package/src/internal/fiberRuntime.ts +3842 -0
- package/src/internal/fiberScope.ts +71 -0
- package/src/internal/fiberStatus.ts +119 -0
- package/src/internal/groupBy.ts +530 -0
- package/src/internal/hashMap/array.ts +49 -0
- package/src/internal/hashMap/bitwise.ts +32 -0
- package/src/internal/hashMap/config.ts +14 -0
- package/src/internal/hashMap/keySet.ts +8 -0
- package/src/internal/hashMap/node.ts +391 -0
- package/src/internal/hashMap.ts +586 -0
- package/src/internal/hashSet.ts +323 -0
- package/src/internal/keyedPool.ts +244 -0
- package/src/internal/layer/circular.ts +214 -0
- package/src/internal/layer.ts +1483 -0
- package/src/internal/logSpan.ts +20 -0
- package/src/internal/logger-circular.ts +24 -0
- package/src/internal/logger.ts +522 -0
- package/src/internal/mailbox.ts +561 -0
- package/src/internal/managedRuntime/circular.ts +6 -0
- package/src/internal/managedRuntime.ts +134 -0
- package/src/internal/matcher.ts +652 -0
- package/src/internal/metric/boundaries.ts +75 -0
- package/src/internal/metric/hook.ts +483 -0
- package/src/internal/metric/key.ts +167 -0
- package/src/internal/metric/keyType.ts +238 -0
- package/src/internal/metric/label.ts +41 -0
- package/src/internal/metric/pair.ts +48 -0
- package/src/internal/metric/polling.ts +149 -0
- package/src/internal/metric/registry.ts +187 -0
- package/src/internal/metric/state.ts +290 -0
- package/src/internal/metric.ts +577 -0
- package/src/internal/opCodes/cause.ts +35 -0
- package/src/internal/opCodes/channel.ts +83 -0
- package/src/internal/opCodes/channelChildExecutorDecision.ts +17 -0
- package/src/internal/opCodes/channelMergeDecision.ts +11 -0
- package/src/internal/opCodes/channelMergeState.ts +17 -0
- package/src/internal/opCodes/channelMergeStrategy.ts +11 -0
- package/src/internal/opCodes/channelState.ts +23 -0
- package/src/internal/opCodes/channelUpstreamPullRequest.ts +11 -0
- package/src/internal/opCodes/channelUpstreamPullStrategy.ts +11 -0
- package/src/internal/opCodes/config.ts +65 -0
- package/src/internal/opCodes/configError.ts +35 -0
- package/src/internal/opCodes/continuation.ts +11 -0
- package/src/internal/opCodes/deferred.ts +11 -0
- package/src/internal/opCodes/effect.ts +89 -0
- package/src/internal/opCodes/layer.ts +59 -0
- package/src/internal/opCodes/streamHaltStrategy.ts +23 -0
- package/src/internal/option.ts +80 -0
- package/src/internal/pool.ts +432 -0
- package/src/internal/pubsub.ts +1762 -0
- package/src/internal/query.ts +204 -0
- package/src/internal/queue.ts +766 -0
- package/src/internal/random.ts +161 -0
- package/src/internal/rateLimiter.ts +93 -0
- package/src/internal/rcMap.ts +285 -0
- package/src/internal/rcRef.ts +192 -0
- package/src/internal/redBlackTree/iterator.ts +200 -0
- package/src/internal/redBlackTree/node.ts +68 -0
- package/src/internal/redBlackTree.ts +1245 -0
- package/src/internal/redacted.ts +73 -0
- package/src/internal/ref.ts +171 -0
- package/src/internal/reloadable.ts +140 -0
- package/src/internal/request.ts +177 -0
- package/src/internal/resource.ts +76 -0
- package/src/internal/ringBuffer.ts +68 -0
- package/src/internal/runtime.ts +558 -0
- package/src/internal/runtimeFlags.ts +178 -0
- package/src/internal/runtimeFlagsPatch.ts +103 -0
- package/src/internal/schedule/decision.ts +47 -0
- package/src/internal/schedule/interval.ts +101 -0
- package/src/internal/schedule/intervals.ts +180 -0
- package/src/internal/schedule.ts +2199 -0
- package/src/internal/schema/errors.ts +191 -0
- package/src/internal/schema/schemaId.ts +106 -0
- package/src/internal/schema/util.ts +50 -0
- package/src/internal/scopedCache.ts +644 -0
- package/src/internal/scopedRef.ts +118 -0
- package/src/internal/secret.ts +89 -0
- package/src/internal/singleShotGen.ts +35 -0
- package/src/internal/sink.ts +2120 -0
- package/src/internal/stack.ts +10 -0
- package/src/internal/stm/core.ts +817 -0
- package/src/internal/stm/entry.ts +59 -0
- package/src/internal/stm/journal.ts +123 -0
- package/src/internal/stm/opCodes/stm.ts +71 -0
- package/src/internal/stm/opCodes/stmState.ts +17 -0
- package/src/internal/stm/opCodes/strategy.ts +17 -0
- package/src/internal/stm/opCodes/tExit.ts +29 -0
- package/src/internal/stm/opCodes/tryCommit.ts +11 -0
- package/src/internal/stm/stm.ts +1453 -0
- package/src/internal/stm/stmState.ts +136 -0
- package/src/internal/stm/tArray.ts +550 -0
- package/src/internal/stm/tDeferred.ts +81 -0
- package/src/internal/stm/tExit.ts +190 -0
- package/src/internal/stm/tMap.ts +824 -0
- package/src/internal/stm/tPriorityQueue.ts +267 -0
- package/src/internal/stm/tPubSub.ts +551 -0
- package/src/internal/stm/tQueue.ts +393 -0
- package/src/internal/stm/tRandom.ts +140 -0
- package/src/internal/stm/tReentrantLock.ts +352 -0
- package/src/internal/stm/tRef.ts +195 -0
- package/src/internal/stm/tSemaphore.ts +113 -0
- package/src/internal/stm/tSet.ts +259 -0
- package/src/internal/stm/tSubscriptionRef.ts +286 -0
- package/src/internal/stm/tryCommit.ts +34 -0
- package/src/internal/stm/txnId.ts +14 -0
- package/src/internal/stm/versioned.ts +4 -0
- package/src/internal/stream/debounceState.ts +57 -0
- package/src/internal/stream/emit.ts +123 -0
- package/src/internal/stream/haltStrategy.ts +94 -0
- package/src/internal/stream/handoff.ts +187 -0
- package/src/internal/stream/handoffSignal.ts +59 -0
- package/src/internal/stream/pull.ts +34 -0
- package/src/internal/stream/sinkEndReason.ts +30 -0
- package/src/internal/stream/zipAllState.ts +88 -0
- package/src/internal/stream/zipChunksState.ts +56 -0
- package/src/internal/stream.ts +8801 -0
- package/src/internal/string-utils.ts +107 -0
- package/src/internal/subscriptionRef.ts +138 -0
- package/src/internal/supervisor/patch.ts +190 -0
- package/src/internal/supervisor.ts +303 -0
- package/src/internal/synchronizedRef.ts +114 -0
- package/src/internal/take.ts +199 -0
- package/src/internal/testing/sleep.ts +27 -0
- package/src/internal/testing/suspendedWarningData.ts +85 -0
- package/src/internal/testing/warningData.ts +94 -0
- package/src/internal/tracer.ts +150 -0
- package/src/internal/trie.ts +722 -0
- package/src/internal/version.ts +7 -0
|
@@ -0,0 +1,1762 @@
|
|
|
1
|
+
import * as Chunk from "../Chunk.js"
|
|
2
|
+
import type * as Deferred from "../Deferred.js"
|
|
3
|
+
import type * as Effect from "../Effect.js"
|
|
4
|
+
import * as Effectable from "../Effectable.js"
|
|
5
|
+
import { dual, pipe } from "../Function.js"
|
|
6
|
+
import * as MutableQueue from "../MutableQueue.js"
|
|
7
|
+
import * as MutableRef from "../MutableRef.js"
|
|
8
|
+
import { nextPow2 } from "../Number.js"
|
|
9
|
+
import * as Option from "../Option.js"
|
|
10
|
+
import { pipeArguments } from "../Pipeable.js"
|
|
11
|
+
import type * as PubSub from "../PubSub.js"
|
|
12
|
+
import type * as Queue from "../Queue.js"
|
|
13
|
+
import type * as Scope from "../Scope.js"
|
|
14
|
+
import * as core from "./core.js"
|
|
15
|
+
import * as executionStrategy from "./executionStrategy.js"
|
|
16
|
+
import * as fiberRuntime from "./fiberRuntime.js"
|
|
17
|
+
import * as queue from "./queue.js"
|
|
18
|
+
|
|
19
|
+
const AbsentValue = Symbol.for("effect/PubSub/AbsentValue")
|
|
20
|
+
type AbsentValue = typeof AbsentValue
|
|
21
|
+
|
|
22
|
+
/** @internal */
|
|
23
|
+
export interface AtomicPubSub<in out A> {
|
|
24
|
+
readonly capacity: number
|
|
25
|
+
isEmpty(): boolean
|
|
26
|
+
isFull(): boolean
|
|
27
|
+
size(): number
|
|
28
|
+
publish(value: A): boolean
|
|
29
|
+
publishAll(elements: Iterable<A>): Chunk.Chunk<A>
|
|
30
|
+
slide(): void
|
|
31
|
+
subscribe(): Subscription<A>
|
|
32
|
+
replayWindow(): ReplayWindow<A>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** @internal */
|
|
36
|
+
interface Subscription<out A> {
|
|
37
|
+
isEmpty(): boolean
|
|
38
|
+
size(): number
|
|
39
|
+
poll<D>(default_: D): A | D
|
|
40
|
+
pollUpTo(n: number): Chunk.Chunk<A>
|
|
41
|
+
unsubscribe(): void
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** @internal */
|
|
45
|
+
type Subscribers<A> = Map<
|
|
46
|
+
Subscription<A>,
|
|
47
|
+
Set<MutableQueue.MutableQueue<Deferred.Deferred<A>>>
|
|
48
|
+
>
|
|
49
|
+
|
|
50
|
+
const addSubscribers = <A>(
|
|
51
|
+
subscription: Subscription<A>,
|
|
52
|
+
pollers: MutableQueue.MutableQueue<Deferred.Deferred<A>>
|
|
53
|
+
) =>
|
|
54
|
+
(subscribers: Subscribers<A>) => {
|
|
55
|
+
if (!subscribers.has(subscription)) {
|
|
56
|
+
subscribers.set(subscription, new Set())
|
|
57
|
+
}
|
|
58
|
+
const set = subscribers.get(subscription)!
|
|
59
|
+
set.add(pollers)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const removeSubscribers = <A>(
|
|
63
|
+
subscription: Subscription<A>,
|
|
64
|
+
pollers: MutableQueue.MutableQueue<Deferred.Deferred<A>>
|
|
65
|
+
) =>
|
|
66
|
+
(subscribers: Subscribers<A>) => {
|
|
67
|
+
if (!subscribers.has(subscription)) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
const set = subscribers.get(subscription)!
|
|
71
|
+
set.delete(pollers)
|
|
72
|
+
if (set.size === 0) {
|
|
73
|
+
subscribers.delete(subscription)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** @internal */
|
|
78
|
+
export const bounded = <A>(
|
|
79
|
+
capacity: number | {
|
|
80
|
+
readonly capacity: number
|
|
81
|
+
readonly replay?: number | undefined
|
|
82
|
+
}
|
|
83
|
+
): Effect.Effect<PubSub.PubSub<A>> =>
|
|
84
|
+
core.suspend(() => {
|
|
85
|
+
const pubsub = makeBoundedPubSub<A>(capacity)
|
|
86
|
+
return makePubSub(pubsub, new BackPressureStrategy())
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
/** @internal */
|
|
90
|
+
export const dropping = <A>(
|
|
91
|
+
capacity: number | {
|
|
92
|
+
readonly capacity: number
|
|
93
|
+
readonly replay?: number | undefined
|
|
94
|
+
}
|
|
95
|
+
): Effect.Effect<PubSub.PubSub<A>> =>
|
|
96
|
+
core.suspend(() => {
|
|
97
|
+
const pubsub = makeBoundedPubSub<A>(capacity)
|
|
98
|
+
return makePubSub(pubsub, new DroppingStrategy())
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
/** @internal */
|
|
102
|
+
export const sliding = <A>(
|
|
103
|
+
capacity: number | {
|
|
104
|
+
readonly capacity: number
|
|
105
|
+
readonly replay?: number | undefined
|
|
106
|
+
}
|
|
107
|
+
): Effect.Effect<PubSub.PubSub<A>> =>
|
|
108
|
+
core.suspend(() => {
|
|
109
|
+
const pubsub = makeBoundedPubSub<A>(capacity)
|
|
110
|
+
return makePubSub(pubsub, new SlidingStrategy())
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
/** @internal */
|
|
114
|
+
export const unbounded = <A>(options?: {
|
|
115
|
+
readonly replay?: number | undefined
|
|
116
|
+
}): Effect.Effect<PubSub.PubSub<A>> =>
|
|
117
|
+
core.suspend(() => {
|
|
118
|
+
const pubsub = makeUnboundedPubSub<A>(options)
|
|
119
|
+
return makePubSub(pubsub, new DroppingStrategy())
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
/** @internal */
|
|
123
|
+
export const capacity = <A>(self: PubSub.PubSub<A>): number => self.capacity()
|
|
124
|
+
|
|
125
|
+
/** @internal */
|
|
126
|
+
export const size = <A>(self: PubSub.PubSub<A>): Effect.Effect<number> => self.size
|
|
127
|
+
|
|
128
|
+
/** @internal */
|
|
129
|
+
export const isFull = <A>(self: PubSub.PubSub<A>): Effect.Effect<boolean> => self.isFull
|
|
130
|
+
|
|
131
|
+
/** @internal */
|
|
132
|
+
export const isEmpty = <A>(self: PubSub.PubSub<A>): Effect.Effect<boolean> => self.isEmpty
|
|
133
|
+
|
|
134
|
+
/** @internal */
|
|
135
|
+
export const shutdown = <A>(self: PubSub.PubSub<A>): Effect.Effect<void> => self.shutdown
|
|
136
|
+
|
|
137
|
+
/** @internal */
|
|
138
|
+
export const isShutdown = <A>(self: PubSub.PubSub<A>): Effect.Effect<boolean> => self.isShutdown
|
|
139
|
+
|
|
140
|
+
/** @internal */
|
|
141
|
+
export const awaitShutdown = <A>(self: PubSub.PubSub<A>): Effect.Effect<void> => self.awaitShutdown
|
|
142
|
+
|
|
143
|
+
/** @internal */
|
|
144
|
+
export const publish = dual<
|
|
145
|
+
<A>(value: A) => (self: PubSub.PubSub<A>) => Effect.Effect<boolean>,
|
|
146
|
+
<A>(self: PubSub.PubSub<A>, value: A) => Effect.Effect<boolean>
|
|
147
|
+
>(2, (self, value) => self.publish(value))
|
|
148
|
+
|
|
149
|
+
/** @internal */
|
|
150
|
+
export const publishAll = dual<
|
|
151
|
+
<A>(elements: Iterable<A>) => (self: PubSub.PubSub<A>) => Effect.Effect<boolean>,
|
|
152
|
+
<A>(self: PubSub.PubSub<A>, elements: Iterable<A>) => Effect.Effect<boolean>
|
|
153
|
+
>(2, (self, elements) => self.publishAll(elements))
|
|
154
|
+
|
|
155
|
+
/** @internal */
|
|
156
|
+
export const subscribe = <A>(self: PubSub.PubSub<A>): Effect.Effect<Queue.Dequeue<A>, never, Scope.Scope> =>
|
|
157
|
+
self.subscribe
|
|
158
|
+
|
|
159
|
+
/** @internal */
|
|
160
|
+
const makeBoundedPubSub = <A>(
|
|
161
|
+
capacity: number | {
|
|
162
|
+
readonly capacity: number
|
|
163
|
+
readonly replay?: number | undefined
|
|
164
|
+
}
|
|
165
|
+
): AtomicPubSub<A> => {
|
|
166
|
+
const options = typeof capacity === "number" ? { capacity } : capacity
|
|
167
|
+
ensureCapacity(options.capacity)
|
|
168
|
+
const replayBuffer = options.replay && options.replay > 0 ? new ReplayBuffer<A>(Math.ceil(options.replay)) : undefined
|
|
169
|
+
if (options.capacity === 1) {
|
|
170
|
+
return new BoundedPubSubSingle(replayBuffer)
|
|
171
|
+
} else if (nextPow2(options.capacity) === options.capacity) {
|
|
172
|
+
return new BoundedPubSubPow2(options.capacity, replayBuffer)
|
|
173
|
+
} else {
|
|
174
|
+
return new BoundedPubSubArb(options.capacity, replayBuffer)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** @internal */
|
|
179
|
+
const makeUnboundedPubSub = <A>(options?: {
|
|
180
|
+
readonly replay?: number | undefined
|
|
181
|
+
}): AtomicPubSub<A> => new UnboundedPubSub(options?.replay ? new ReplayBuffer(options.replay) : undefined)
|
|
182
|
+
|
|
183
|
+
/** @internal */
|
|
184
|
+
const makeSubscription = <A>(
|
|
185
|
+
pubsub: AtomicPubSub<A>,
|
|
186
|
+
subscribers: Subscribers<A>,
|
|
187
|
+
strategy: PubSubStrategy<A>
|
|
188
|
+
): Effect.Effect<Queue.Dequeue<A>> =>
|
|
189
|
+
core.map(core.deferredMake<void>(), (deferred) =>
|
|
190
|
+
unsafeMakeSubscription(
|
|
191
|
+
pubsub,
|
|
192
|
+
subscribers,
|
|
193
|
+
pubsub.subscribe(),
|
|
194
|
+
MutableQueue.unbounded<Deferred.Deferred<A>>(),
|
|
195
|
+
deferred,
|
|
196
|
+
MutableRef.make(false),
|
|
197
|
+
strategy
|
|
198
|
+
))
|
|
199
|
+
|
|
200
|
+
/** @internal */
|
|
201
|
+
export const unsafeMakeSubscription = <A>(
|
|
202
|
+
pubsub: AtomicPubSub<A>,
|
|
203
|
+
subscribers: Subscribers<A>,
|
|
204
|
+
subscription: Subscription<A>,
|
|
205
|
+
pollers: MutableQueue.MutableQueue<Deferred.Deferred<A>>,
|
|
206
|
+
shutdownHook: Deferred.Deferred<void>,
|
|
207
|
+
shutdownFlag: MutableRef.MutableRef<boolean>,
|
|
208
|
+
strategy: PubSubStrategy<A>
|
|
209
|
+
): Queue.Dequeue<A> =>
|
|
210
|
+
new SubscriptionImpl(
|
|
211
|
+
pubsub,
|
|
212
|
+
subscribers,
|
|
213
|
+
subscription,
|
|
214
|
+
pollers,
|
|
215
|
+
shutdownHook,
|
|
216
|
+
shutdownFlag,
|
|
217
|
+
strategy,
|
|
218
|
+
pubsub.replayWindow()
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
/** @internal */
|
|
222
|
+
class BoundedPubSubArb<in out A> implements AtomicPubSub<A> {
|
|
223
|
+
array: Array<A>
|
|
224
|
+
publisherIndex = 0
|
|
225
|
+
subscribers: Array<number>
|
|
226
|
+
subscriberCount = 0
|
|
227
|
+
subscribersIndex = 0
|
|
228
|
+
|
|
229
|
+
constructor(readonly capacity: number, readonly replayBuffer: ReplayBuffer<A> | undefined) {
|
|
230
|
+
this.array = Array.from({ length: capacity })
|
|
231
|
+
this.subscribers = Array.from({ length: capacity })
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
replayWindow(): ReplayWindow<A> {
|
|
235
|
+
return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
isEmpty(): boolean {
|
|
239
|
+
return this.publisherIndex === this.subscribersIndex
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
isFull(): boolean {
|
|
243
|
+
return this.publisherIndex === this.subscribersIndex + this.capacity
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
size(): number {
|
|
247
|
+
return this.publisherIndex - this.subscribersIndex
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
publish(value: A): boolean {
|
|
251
|
+
if (this.isFull()) {
|
|
252
|
+
return false
|
|
253
|
+
}
|
|
254
|
+
if (this.subscriberCount !== 0) {
|
|
255
|
+
const index = this.publisherIndex % this.capacity
|
|
256
|
+
this.array[index] = value
|
|
257
|
+
this.subscribers[index] = this.subscriberCount
|
|
258
|
+
this.publisherIndex += 1
|
|
259
|
+
}
|
|
260
|
+
if (this.replayBuffer) {
|
|
261
|
+
this.replayBuffer.offer(value)
|
|
262
|
+
}
|
|
263
|
+
return true
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
publishAll(elements: Iterable<A>): Chunk.Chunk<A> {
|
|
267
|
+
if (this.subscriberCount === 0) {
|
|
268
|
+
if (this.replayBuffer) {
|
|
269
|
+
this.replayBuffer.offerAll(elements)
|
|
270
|
+
}
|
|
271
|
+
return Chunk.empty()
|
|
272
|
+
}
|
|
273
|
+
const chunk = Chunk.fromIterable(elements)
|
|
274
|
+
const n = chunk.length
|
|
275
|
+
const size = this.publisherIndex - this.subscribersIndex
|
|
276
|
+
const available = this.capacity - size
|
|
277
|
+
const forPubSub = Math.min(n, available)
|
|
278
|
+
if (forPubSub === 0) {
|
|
279
|
+
return chunk
|
|
280
|
+
}
|
|
281
|
+
let iteratorIndex = 0
|
|
282
|
+
const publishAllIndex = this.publisherIndex + forPubSub
|
|
283
|
+
while (this.publisherIndex !== publishAllIndex) {
|
|
284
|
+
const a = Chunk.unsafeGet(chunk, iteratorIndex++)
|
|
285
|
+
const index = this.publisherIndex % this.capacity
|
|
286
|
+
this.array[index] = a
|
|
287
|
+
this.subscribers[index] = this.subscriberCount
|
|
288
|
+
this.publisherIndex += 1
|
|
289
|
+
if (this.replayBuffer) {
|
|
290
|
+
this.replayBuffer.offer(a)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return Chunk.drop(chunk, iteratorIndex)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
slide(): void {
|
|
297
|
+
if (this.subscribersIndex !== this.publisherIndex) {
|
|
298
|
+
const index = this.subscribersIndex % this.capacity
|
|
299
|
+
this.array[index] = AbsentValue as unknown as A
|
|
300
|
+
this.subscribers[index] = 0
|
|
301
|
+
this.subscribersIndex += 1
|
|
302
|
+
}
|
|
303
|
+
if (this.replayBuffer) {
|
|
304
|
+
this.replayBuffer.slide()
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
subscribe(): Subscription<A> {
|
|
309
|
+
this.subscriberCount += 1
|
|
310
|
+
return new BoundedPubSubArbSubscription(this, this.publisherIndex, false)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
class BoundedPubSubArbSubscription<in out A> implements Subscription<A> {
|
|
315
|
+
constructor(
|
|
316
|
+
private self: BoundedPubSubArb<A>,
|
|
317
|
+
private subscriberIndex: number,
|
|
318
|
+
private unsubscribed: boolean
|
|
319
|
+
) {
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
isEmpty(): boolean {
|
|
323
|
+
return (
|
|
324
|
+
this.unsubscribed ||
|
|
325
|
+
this.self.publisherIndex === this.subscriberIndex ||
|
|
326
|
+
this.self.publisherIndex === this.self.subscribersIndex
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
size() {
|
|
331
|
+
if (this.unsubscribed) {
|
|
332
|
+
return 0
|
|
333
|
+
}
|
|
334
|
+
return this.self.publisherIndex - Math.max(this.subscriberIndex, this.self.subscribersIndex)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
poll<D>(default_: D): A | D {
|
|
338
|
+
if (this.unsubscribed) {
|
|
339
|
+
return default_
|
|
340
|
+
}
|
|
341
|
+
this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex)
|
|
342
|
+
if (this.subscriberIndex !== this.self.publisherIndex) {
|
|
343
|
+
const index = this.subscriberIndex % this.self.capacity
|
|
344
|
+
const elem = this.self.array[index]!
|
|
345
|
+
this.self.subscribers[index] -= 1
|
|
346
|
+
if (this.self.subscribers[index] === 0) {
|
|
347
|
+
this.self.array[index] = AbsentValue as unknown as A
|
|
348
|
+
this.self.subscribersIndex += 1
|
|
349
|
+
}
|
|
350
|
+
this.subscriberIndex += 1
|
|
351
|
+
return elem
|
|
352
|
+
}
|
|
353
|
+
return default_
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
pollUpTo(n: number): Chunk.Chunk<A> {
|
|
357
|
+
if (this.unsubscribed) {
|
|
358
|
+
return Chunk.empty()
|
|
359
|
+
}
|
|
360
|
+
this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex)
|
|
361
|
+
const size = this.self.publisherIndex - this.subscriberIndex
|
|
362
|
+
const toPoll = Math.min(n, size)
|
|
363
|
+
if (toPoll <= 0) {
|
|
364
|
+
return Chunk.empty()
|
|
365
|
+
}
|
|
366
|
+
const builder: Array<A> = []
|
|
367
|
+
const pollUpToIndex = this.subscriberIndex + toPoll
|
|
368
|
+
while (this.subscriberIndex !== pollUpToIndex) {
|
|
369
|
+
const index = this.subscriberIndex % this.self.capacity
|
|
370
|
+
const a = this.self.array[index] as A
|
|
371
|
+
this.self.subscribers[index] -= 1
|
|
372
|
+
if (this.self.subscribers[index] === 0) {
|
|
373
|
+
this.self.array[index] = AbsentValue as unknown as A
|
|
374
|
+
this.self.subscribersIndex += 1
|
|
375
|
+
}
|
|
376
|
+
builder.push(a)
|
|
377
|
+
this.subscriberIndex += 1
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return Chunk.fromIterable(builder)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
unsubscribe(): void {
|
|
384
|
+
if (!this.unsubscribed) {
|
|
385
|
+
this.unsubscribed = true
|
|
386
|
+
this.self.subscriberCount -= 1
|
|
387
|
+
this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex)
|
|
388
|
+
while (this.subscriberIndex !== this.self.publisherIndex) {
|
|
389
|
+
const index = this.subscriberIndex % this.self.capacity
|
|
390
|
+
this.self.subscribers[index] -= 1
|
|
391
|
+
if (this.self.subscribers[index] === 0) {
|
|
392
|
+
this.self.array[index] = AbsentValue as unknown as A
|
|
393
|
+
this.self.subscribersIndex += 1
|
|
394
|
+
}
|
|
395
|
+
this.subscriberIndex += 1
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** @internal */
|
|
402
|
+
class BoundedPubSubPow2<in out A> implements AtomicPubSub<A> {
|
|
403
|
+
array: Array<A>
|
|
404
|
+
mask: number
|
|
405
|
+
publisherIndex = 0
|
|
406
|
+
subscribers: Array<number>
|
|
407
|
+
subscriberCount = 0
|
|
408
|
+
subscribersIndex = 0
|
|
409
|
+
|
|
410
|
+
constructor(readonly capacity: number, readonly replayBuffer: ReplayBuffer<A> | undefined) {
|
|
411
|
+
this.array = Array.from({ length: capacity })
|
|
412
|
+
this.mask = capacity - 1
|
|
413
|
+
this.subscribers = Array.from({ length: capacity })
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
replayWindow(): ReplayWindow<A> {
|
|
417
|
+
return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
isEmpty(): boolean {
|
|
421
|
+
return this.publisherIndex === this.subscribersIndex
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
isFull(): boolean {
|
|
425
|
+
return this.publisherIndex === this.subscribersIndex + this.capacity
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
size(): number {
|
|
429
|
+
return this.publisherIndex - this.subscribersIndex
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
publish(value: A): boolean {
|
|
433
|
+
if (this.isFull()) {
|
|
434
|
+
return false
|
|
435
|
+
}
|
|
436
|
+
if (this.subscriberCount !== 0) {
|
|
437
|
+
const index = this.publisherIndex & this.mask
|
|
438
|
+
this.array[index] = value
|
|
439
|
+
this.subscribers[index] = this.subscriberCount
|
|
440
|
+
this.publisherIndex += 1
|
|
441
|
+
}
|
|
442
|
+
if (this.replayBuffer) {
|
|
443
|
+
this.replayBuffer.offer(value)
|
|
444
|
+
}
|
|
445
|
+
return true
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
publishAll(elements: Iterable<A>): Chunk.Chunk<A> {
|
|
449
|
+
if (this.subscriberCount === 0) {
|
|
450
|
+
if (this.replayBuffer) {
|
|
451
|
+
this.replayBuffer.offerAll(elements)
|
|
452
|
+
}
|
|
453
|
+
return Chunk.empty()
|
|
454
|
+
}
|
|
455
|
+
const chunk = Chunk.fromIterable(elements)
|
|
456
|
+
const n = chunk.length
|
|
457
|
+
const size = this.publisherIndex - this.subscribersIndex
|
|
458
|
+
const available = this.capacity - size
|
|
459
|
+
const forPubSub = Math.min(n, available)
|
|
460
|
+
if (forPubSub === 0) {
|
|
461
|
+
return chunk
|
|
462
|
+
}
|
|
463
|
+
let iteratorIndex = 0
|
|
464
|
+
const publishAllIndex = this.publisherIndex + forPubSub
|
|
465
|
+
while (this.publisherIndex !== publishAllIndex) {
|
|
466
|
+
const elem = Chunk.unsafeGet(chunk, iteratorIndex++)
|
|
467
|
+
const index = this.publisherIndex & this.mask
|
|
468
|
+
this.array[index] = elem
|
|
469
|
+
this.subscribers[index] = this.subscriberCount
|
|
470
|
+
this.publisherIndex += 1
|
|
471
|
+
if (this.replayBuffer) {
|
|
472
|
+
this.replayBuffer.offer(elem)
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return Chunk.drop(chunk, iteratorIndex)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
slide(): void {
|
|
479
|
+
if (this.subscribersIndex !== this.publisherIndex) {
|
|
480
|
+
const index = this.subscribersIndex & this.mask
|
|
481
|
+
this.array[index] = AbsentValue as unknown as A
|
|
482
|
+
this.subscribers[index] = 0
|
|
483
|
+
this.subscribersIndex += 1
|
|
484
|
+
}
|
|
485
|
+
if (this.replayBuffer) {
|
|
486
|
+
this.replayBuffer.slide()
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
subscribe(): Subscription<A> {
|
|
491
|
+
this.subscriberCount += 1
|
|
492
|
+
return new BoundedPubSubPow2Subscription(this, this.publisherIndex, false)
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** @internal */
|
|
497
|
+
class BoundedPubSubPow2Subscription<in out A> implements Subscription<A> {
|
|
498
|
+
constructor(
|
|
499
|
+
private self: BoundedPubSubPow2<A>,
|
|
500
|
+
private subscriberIndex: number,
|
|
501
|
+
private unsubscribed: boolean
|
|
502
|
+
) {
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
isEmpty(): boolean {
|
|
506
|
+
return (
|
|
507
|
+
this.unsubscribed ||
|
|
508
|
+
this.self.publisherIndex === this.subscriberIndex ||
|
|
509
|
+
this.self.publisherIndex === this.self.subscribersIndex
|
|
510
|
+
)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
size() {
|
|
514
|
+
if (this.unsubscribed) {
|
|
515
|
+
return 0
|
|
516
|
+
}
|
|
517
|
+
return this.self.publisherIndex - Math.max(this.subscriberIndex, this.self.subscribersIndex)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
poll<D>(default_: D): A | D {
|
|
521
|
+
if (this.unsubscribed) {
|
|
522
|
+
return default_
|
|
523
|
+
}
|
|
524
|
+
this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex)
|
|
525
|
+
if (this.subscriberIndex !== this.self.publisherIndex) {
|
|
526
|
+
const index = this.subscriberIndex & this.self.mask
|
|
527
|
+
const elem = this.self.array[index]!
|
|
528
|
+
this.self.subscribers[index] -= 1
|
|
529
|
+
if (this.self.subscribers[index] === 0) {
|
|
530
|
+
this.self.array[index] = AbsentValue as unknown as A
|
|
531
|
+
this.self.subscribersIndex += 1
|
|
532
|
+
}
|
|
533
|
+
this.subscriberIndex += 1
|
|
534
|
+
return elem
|
|
535
|
+
}
|
|
536
|
+
return default_
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
pollUpTo(n: number): Chunk.Chunk<A> {
|
|
540
|
+
if (this.unsubscribed) {
|
|
541
|
+
return Chunk.empty()
|
|
542
|
+
}
|
|
543
|
+
this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex)
|
|
544
|
+
const size = this.self.publisherIndex - this.subscriberIndex
|
|
545
|
+
const toPoll = Math.min(n, size)
|
|
546
|
+
if (toPoll <= 0) {
|
|
547
|
+
return Chunk.empty()
|
|
548
|
+
}
|
|
549
|
+
const builder: Array<A> = []
|
|
550
|
+
const pollUpToIndex = this.subscriberIndex + toPoll
|
|
551
|
+
while (this.subscriberIndex !== pollUpToIndex) {
|
|
552
|
+
const index = this.subscriberIndex & this.self.mask
|
|
553
|
+
const elem = this.self.array[index] as A
|
|
554
|
+
this.self.subscribers[index] -= 1
|
|
555
|
+
if (this.self.subscribers[index] === 0) {
|
|
556
|
+
this.self.array[index] = AbsentValue as unknown as A
|
|
557
|
+
this.self.subscribersIndex += 1
|
|
558
|
+
}
|
|
559
|
+
builder.push(elem)
|
|
560
|
+
this.subscriberIndex += 1
|
|
561
|
+
}
|
|
562
|
+
return Chunk.fromIterable(builder)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
unsubscribe(): void {
|
|
566
|
+
if (!this.unsubscribed) {
|
|
567
|
+
this.unsubscribed = true
|
|
568
|
+
this.self.subscriberCount -= 1
|
|
569
|
+
this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex)
|
|
570
|
+
while (this.subscriberIndex !== this.self.publisherIndex) {
|
|
571
|
+
const index = this.subscriberIndex & this.self.mask
|
|
572
|
+
this.self.subscribers[index] -= 1
|
|
573
|
+
if (this.self.subscribers[index] === 0) {
|
|
574
|
+
this.self.array[index] = AbsentValue as unknown as A
|
|
575
|
+
this.self.subscribersIndex += 1
|
|
576
|
+
}
|
|
577
|
+
this.subscriberIndex += 1
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/** @internal */
|
|
584
|
+
class BoundedPubSubSingle<in out A> implements AtomicPubSub<A> {
|
|
585
|
+
publisherIndex = 0
|
|
586
|
+
subscriberCount = 0
|
|
587
|
+
subscribers = 0
|
|
588
|
+
value: A = AbsentValue as unknown as A
|
|
589
|
+
|
|
590
|
+
readonly capacity = 1
|
|
591
|
+
constructor(readonly replayBuffer: ReplayBuffer<A> | undefined) {}
|
|
592
|
+
|
|
593
|
+
replayWindow(): ReplayWindow<A> {
|
|
594
|
+
return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
pipe() {
|
|
598
|
+
return pipeArguments(this, arguments)
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
isEmpty(): boolean {
|
|
602
|
+
return this.subscribers === 0
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
isFull(): boolean {
|
|
606
|
+
return !this.isEmpty()
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
size(): number {
|
|
610
|
+
return this.isEmpty() ? 0 : 1
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
publish(value: A): boolean {
|
|
614
|
+
if (this.isFull()) {
|
|
615
|
+
return false
|
|
616
|
+
}
|
|
617
|
+
if (this.subscriberCount !== 0) {
|
|
618
|
+
this.value = value
|
|
619
|
+
this.subscribers = this.subscriberCount
|
|
620
|
+
this.publisherIndex += 1
|
|
621
|
+
}
|
|
622
|
+
if (this.replayBuffer) {
|
|
623
|
+
this.replayBuffer.offer(value)
|
|
624
|
+
}
|
|
625
|
+
return true
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
publishAll(elements: Iterable<A>): Chunk.Chunk<A> {
|
|
629
|
+
if (this.subscriberCount === 0) {
|
|
630
|
+
if (this.replayBuffer) {
|
|
631
|
+
this.replayBuffer.offerAll(elements)
|
|
632
|
+
}
|
|
633
|
+
return Chunk.empty()
|
|
634
|
+
}
|
|
635
|
+
const chunk = Chunk.fromIterable(elements)
|
|
636
|
+
if (Chunk.isEmpty(chunk)) {
|
|
637
|
+
return chunk
|
|
638
|
+
}
|
|
639
|
+
if (this.publish(Chunk.unsafeHead(chunk))) {
|
|
640
|
+
return Chunk.drop(chunk, 1)
|
|
641
|
+
} else {
|
|
642
|
+
return chunk
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
slide(): void {
|
|
647
|
+
if (this.isFull()) {
|
|
648
|
+
this.subscribers = 0
|
|
649
|
+
this.value = AbsentValue as unknown as A
|
|
650
|
+
}
|
|
651
|
+
if (this.replayBuffer) {
|
|
652
|
+
this.replayBuffer.slide()
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
subscribe(): Subscription<A> {
|
|
657
|
+
this.subscriberCount += 1
|
|
658
|
+
return new BoundedPubSubSingleSubscription(this, this.publisherIndex, false)
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/** @internal */
|
|
663
|
+
class BoundedPubSubSingleSubscription<in out A> implements Subscription<A> {
|
|
664
|
+
constructor(
|
|
665
|
+
private self: BoundedPubSubSingle<A>,
|
|
666
|
+
private subscriberIndex: number,
|
|
667
|
+
private unsubscribed: boolean
|
|
668
|
+
) {
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
isEmpty(): boolean {
|
|
672
|
+
return (
|
|
673
|
+
this.unsubscribed ||
|
|
674
|
+
this.self.subscribers === 0 ||
|
|
675
|
+
this.subscriberIndex === this.self.publisherIndex
|
|
676
|
+
)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
size() {
|
|
680
|
+
return this.isEmpty() ? 0 : 1
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
poll<D>(default_: D): A | D {
|
|
684
|
+
if (this.isEmpty()) {
|
|
685
|
+
return default_
|
|
686
|
+
}
|
|
687
|
+
const elem = this.self.value
|
|
688
|
+
this.self.subscribers -= 1
|
|
689
|
+
if (this.self.subscribers === 0) {
|
|
690
|
+
this.self.value = AbsentValue as unknown as A
|
|
691
|
+
}
|
|
692
|
+
this.subscriberIndex += 1
|
|
693
|
+
return elem
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
pollUpTo(n: number): Chunk.Chunk<A> {
|
|
697
|
+
if (this.isEmpty() || n < 1) {
|
|
698
|
+
return Chunk.empty()
|
|
699
|
+
}
|
|
700
|
+
const a = this.self.value
|
|
701
|
+
this.self.subscribers -= 1
|
|
702
|
+
if (this.self.subscribers === 0) {
|
|
703
|
+
this.self.value = AbsentValue as unknown as A
|
|
704
|
+
}
|
|
705
|
+
this.subscriberIndex += 1
|
|
706
|
+
return Chunk.of(a)
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
unsubscribe(): void {
|
|
710
|
+
if (!this.unsubscribed) {
|
|
711
|
+
this.unsubscribed = true
|
|
712
|
+
this.self.subscriberCount -= 1
|
|
713
|
+
if (this.subscriberIndex !== this.self.publisherIndex) {
|
|
714
|
+
this.self.subscribers -= 1
|
|
715
|
+
if (this.self.subscribers === 0) {
|
|
716
|
+
this.self.value = AbsentValue as unknown as A
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/** @internal */
|
|
724
|
+
interface Node<out A> {
|
|
725
|
+
value: A | AbsentValue
|
|
726
|
+
subscribers: number
|
|
727
|
+
next: Node<A> | null
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/** @internal */
|
|
731
|
+
class UnboundedPubSub<in out A> implements AtomicPubSub<A> {
|
|
732
|
+
publisherHead: Node<A> = {
|
|
733
|
+
value: AbsentValue,
|
|
734
|
+
subscribers: 0,
|
|
735
|
+
next: null
|
|
736
|
+
}
|
|
737
|
+
publisherTail = this.publisherHead
|
|
738
|
+
publisherIndex = 0
|
|
739
|
+
subscribersIndex = 0
|
|
740
|
+
|
|
741
|
+
readonly capacity = Number.MAX_SAFE_INTEGER
|
|
742
|
+
constructor(readonly replayBuffer: ReplayBuffer<A> | undefined) {}
|
|
743
|
+
|
|
744
|
+
replayWindow(): ReplayWindow<A> {
|
|
745
|
+
return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
isEmpty(): boolean {
|
|
749
|
+
return this.publisherHead === this.publisherTail
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
isFull(): boolean {
|
|
753
|
+
return false
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
size(): number {
|
|
757
|
+
return this.publisherIndex - this.subscribersIndex
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
publish(value: A): boolean {
|
|
761
|
+
const subscribers = this.publisherTail.subscribers
|
|
762
|
+
if (subscribers !== 0) {
|
|
763
|
+
this.publisherTail.next = {
|
|
764
|
+
value,
|
|
765
|
+
subscribers,
|
|
766
|
+
next: null
|
|
767
|
+
}
|
|
768
|
+
this.publisherTail = this.publisherTail.next
|
|
769
|
+
this.publisherIndex += 1
|
|
770
|
+
}
|
|
771
|
+
if (this.replayBuffer) {
|
|
772
|
+
this.replayBuffer.offer(value)
|
|
773
|
+
}
|
|
774
|
+
return true
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
publishAll(elements: Iterable<A>): Chunk.Chunk<A> {
|
|
778
|
+
if (this.publisherTail.subscribers !== 0) {
|
|
779
|
+
for (const a of elements) {
|
|
780
|
+
this.publish(a)
|
|
781
|
+
}
|
|
782
|
+
} else if (this.replayBuffer) {
|
|
783
|
+
this.replayBuffer.offerAll(elements)
|
|
784
|
+
}
|
|
785
|
+
return Chunk.empty()
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
slide(): void {
|
|
789
|
+
if (this.publisherHead !== this.publisherTail) {
|
|
790
|
+
this.publisherHead = this.publisherHead.next!
|
|
791
|
+
this.publisherHead.value = AbsentValue
|
|
792
|
+
this.subscribersIndex += 1
|
|
793
|
+
}
|
|
794
|
+
if (this.replayBuffer) {
|
|
795
|
+
this.replayBuffer.slide()
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
subscribe(): Subscription<A> {
|
|
800
|
+
this.publisherTail.subscribers += 1
|
|
801
|
+
return new UnboundedPubSubSubscription(
|
|
802
|
+
this,
|
|
803
|
+
this.publisherTail,
|
|
804
|
+
this.publisherIndex,
|
|
805
|
+
false
|
|
806
|
+
)
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/** @internal */
|
|
811
|
+
class UnboundedPubSubSubscription<in out A> implements Subscription<A> {
|
|
812
|
+
constructor(
|
|
813
|
+
private self: UnboundedPubSub<A>,
|
|
814
|
+
private subscriberHead: Node<A>,
|
|
815
|
+
private subscriberIndex: number,
|
|
816
|
+
private unsubscribed: boolean
|
|
817
|
+
) {
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
isEmpty(): boolean {
|
|
821
|
+
if (this.unsubscribed) {
|
|
822
|
+
return true
|
|
823
|
+
}
|
|
824
|
+
let empty = true
|
|
825
|
+
let loop = true
|
|
826
|
+
while (loop) {
|
|
827
|
+
if (this.subscriberHead === this.self.publisherTail) {
|
|
828
|
+
loop = false
|
|
829
|
+
} else {
|
|
830
|
+
if (this.subscriberHead.next!.value !== AbsentValue) {
|
|
831
|
+
empty = false
|
|
832
|
+
loop = false
|
|
833
|
+
} else {
|
|
834
|
+
this.subscriberHead = this.subscriberHead.next!
|
|
835
|
+
this.subscriberIndex += 1
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return empty
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
size() {
|
|
843
|
+
if (this.unsubscribed) {
|
|
844
|
+
return 0
|
|
845
|
+
}
|
|
846
|
+
return this.self.publisherIndex - Math.max(this.subscriberIndex, this.self.subscribersIndex)
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
poll<D>(default_: D): A | D {
|
|
850
|
+
if (this.unsubscribed) {
|
|
851
|
+
return default_
|
|
852
|
+
}
|
|
853
|
+
let loop = true
|
|
854
|
+
let polled: A | D = default_
|
|
855
|
+
while (loop) {
|
|
856
|
+
if (this.subscriberHead === this.self.publisherTail) {
|
|
857
|
+
loop = false
|
|
858
|
+
} else {
|
|
859
|
+
const elem = this.subscriberHead.next!.value
|
|
860
|
+
if (elem !== AbsentValue) {
|
|
861
|
+
polled = elem
|
|
862
|
+
this.subscriberHead.subscribers -= 1
|
|
863
|
+
if (this.subscriberHead.subscribers === 0) {
|
|
864
|
+
this.self.publisherHead = this.self.publisherHead.next!
|
|
865
|
+
this.self.publisherHead.value = AbsentValue
|
|
866
|
+
this.self.subscribersIndex += 1
|
|
867
|
+
}
|
|
868
|
+
loop = false
|
|
869
|
+
}
|
|
870
|
+
this.subscriberHead = this.subscriberHead.next!
|
|
871
|
+
this.subscriberIndex += 1
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return polled
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
pollUpTo(n: number): Chunk.Chunk<A> {
|
|
878
|
+
const builder: Array<A> = []
|
|
879
|
+
const default_ = AbsentValue
|
|
880
|
+
let i = 0
|
|
881
|
+
while (i !== n) {
|
|
882
|
+
const a = this.poll(default_ as unknown as A)
|
|
883
|
+
if (a === default_) {
|
|
884
|
+
i = n
|
|
885
|
+
} else {
|
|
886
|
+
builder.push(a)
|
|
887
|
+
i += 1
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return Chunk.fromIterable(builder)
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
unsubscribe(): void {
|
|
894
|
+
if (!this.unsubscribed) {
|
|
895
|
+
this.unsubscribed = true
|
|
896
|
+
this.self.publisherTail.subscribers -= 1
|
|
897
|
+
while (this.subscriberHead !== this.self.publisherTail) {
|
|
898
|
+
if (this.subscriberHead.next!.value !== AbsentValue) {
|
|
899
|
+
this.subscriberHead.subscribers -= 1
|
|
900
|
+
if (this.subscriberHead.subscribers === 0) {
|
|
901
|
+
this.self.publisherHead = this.self.publisherHead.next!
|
|
902
|
+
this.self.publisherHead.value = AbsentValue
|
|
903
|
+
this.self.subscribersIndex += 1
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
this.subscriberHead = this.subscriberHead.next!
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/** @internal */
|
|
913
|
+
class SubscriptionImpl<in out A> extends Effectable.Class<A> implements Queue.Dequeue<A> {
|
|
914
|
+
[queue.DequeueTypeId] = queue.dequeueVariance
|
|
915
|
+
|
|
916
|
+
constructor(
|
|
917
|
+
readonly pubsub: AtomicPubSub<A>,
|
|
918
|
+
readonly subscribers: Subscribers<A>,
|
|
919
|
+
readonly subscription: Subscription<A>,
|
|
920
|
+
readonly pollers: MutableQueue.MutableQueue<Deferred.Deferred<A>>,
|
|
921
|
+
readonly shutdownHook: Deferred.Deferred<void>,
|
|
922
|
+
readonly shutdownFlag: MutableRef.MutableRef<boolean>,
|
|
923
|
+
readonly strategy: PubSubStrategy<A>,
|
|
924
|
+
readonly replayWindow: ReplayWindow<A>
|
|
925
|
+
) {
|
|
926
|
+
super()
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
commit() {
|
|
930
|
+
return this.take
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
pipe() {
|
|
934
|
+
return pipeArguments(this, arguments)
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
capacity(): number {
|
|
938
|
+
return this.pubsub.capacity
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
isActive(): boolean {
|
|
942
|
+
return !MutableRef.get(this.shutdownFlag)
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
get size(): Effect.Effect<number> {
|
|
946
|
+
return core.suspend(() =>
|
|
947
|
+
MutableRef.get(this.shutdownFlag)
|
|
948
|
+
? core.interrupt
|
|
949
|
+
: core.succeed(this.subscription.size() + this.replayWindow.remaining)
|
|
950
|
+
)
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
unsafeSize(): Option.Option<number> {
|
|
954
|
+
if (MutableRef.get(this.shutdownFlag)) {
|
|
955
|
+
return Option.none()
|
|
956
|
+
}
|
|
957
|
+
return Option.some(this.subscription.size() + this.replayWindow.remaining)
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
get isFull(): Effect.Effect<boolean> {
|
|
961
|
+
return core.suspend(() =>
|
|
962
|
+
MutableRef.get(this.shutdownFlag)
|
|
963
|
+
? core.interrupt
|
|
964
|
+
: core.succeed(this.subscription.size() === this.capacity())
|
|
965
|
+
)
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
get isEmpty(): Effect.Effect<boolean> {
|
|
969
|
+
return core.map(this.size, (size) => size === 0)
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
get shutdown(): Effect.Effect<void> {
|
|
973
|
+
return core.uninterruptible(
|
|
974
|
+
core.withFiberRuntime<void>((state) => {
|
|
975
|
+
MutableRef.set(this.shutdownFlag, true)
|
|
976
|
+
return pipe(
|
|
977
|
+
fiberRuntime.forEachParUnbounded(
|
|
978
|
+
unsafePollAllQueue(this.pollers),
|
|
979
|
+
(d) => core.deferredInterruptWith(d, state.id()),
|
|
980
|
+
false
|
|
981
|
+
),
|
|
982
|
+
core.zipRight(core.sync(() => {
|
|
983
|
+
this.subscribers.delete(this.subscription)
|
|
984
|
+
this.subscription.unsubscribe()
|
|
985
|
+
this.strategy.unsafeOnPubSubEmptySpace(this.pubsub, this.subscribers)
|
|
986
|
+
})),
|
|
987
|
+
core.whenEffect(core.deferredSucceed(this.shutdownHook, void 0)),
|
|
988
|
+
core.asVoid
|
|
989
|
+
)
|
|
990
|
+
})
|
|
991
|
+
)
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
get isShutdown(): Effect.Effect<boolean> {
|
|
995
|
+
return core.sync(() => MutableRef.get(this.shutdownFlag))
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
get awaitShutdown(): Effect.Effect<void> {
|
|
999
|
+
return core.deferredAwait(this.shutdownHook)
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
get take(): Effect.Effect<A> {
|
|
1003
|
+
return core.withFiberRuntime((state) => {
|
|
1004
|
+
if (MutableRef.get(this.shutdownFlag)) {
|
|
1005
|
+
return core.interrupt
|
|
1006
|
+
}
|
|
1007
|
+
if (this.replayWindow.remaining > 0) {
|
|
1008
|
+
const message = this.replayWindow.take()!
|
|
1009
|
+
return core.succeed(message)
|
|
1010
|
+
}
|
|
1011
|
+
const message = MutableQueue.isEmpty(this.pollers)
|
|
1012
|
+
? this.subscription.poll(MutableQueue.EmptyMutableQueue)
|
|
1013
|
+
: MutableQueue.EmptyMutableQueue
|
|
1014
|
+
if (message === MutableQueue.EmptyMutableQueue) {
|
|
1015
|
+
const deferred = core.deferredUnsafeMake<A>(state.id())
|
|
1016
|
+
return pipe(
|
|
1017
|
+
core.suspend(() => {
|
|
1018
|
+
pipe(this.pollers, MutableQueue.offer(deferred))
|
|
1019
|
+
pipe(this.subscribers, addSubscribers(this.subscription, this.pollers))
|
|
1020
|
+
this.strategy.unsafeCompletePollers(
|
|
1021
|
+
this.pubsub,
|
|
1022
|
+
this.subscribers,
|
|
1023
|
+
this.subscription,
|
|
1024
|
+
this.pollers
|
|
1025
|
+
)
|
|
1026
|
+
return MutableRef.get(this.shutdownFlag) ? core.interrupt : core.deferredAwait(deferred)
|
|
1027
|
+
}),
|
|
1028
|
+
core.onInterrupt(() => core.sync(() => unsafeRemove(this.pollers, deferred)))
|
|
1029
|
+
)
|
|
1030
|
+
} else {
|
|
1031
|
+
this.strategy.unsafeOnPubSubEmptySpace(this.pubsub, this.subscribers)
|
|
1032
|
+
return core.succeed(message)
|
|
1033
|
+
}
|
|
1034
|
+
})
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
get takeAll(): Effect.Effect<Chunk.Chunk<A>> {
|
|
1038
|
+
return core.suspend(() => {
|
|
1039
|
+
if (MutableRef.get(this.shutdownFlag)) {
|
|
1040
|
+
return core.interrupt
|
|
1041
|
+
}
|
|
1042
|
+
const as = MutableQueue.isEmpty(this.pollers)
|
|
1043
|
+
? unsafePollAllSubscription(this.subscription)
|
|
1044
|
+
: Chunk.empty()
|
|
1045
|
+
this.strategy.unsafeOnPubSubEmptySpace(this.pubsub, this.subscribers)
|
|
1046
|
+
if (this.replayWindow.remaining > 0) {
|
|
1047
|
+
return core.succeed(Chunk.appendAll(this.replayWindow.takeAll(), as))
|
|
1048
|
+
}
|
|
1049
|
+
return core.succeed(as)
|
|
1050
|
+
})
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
takeUpTo(this: this, max: number): Effect.Effect<Chunk.Chunk<A>> {
|
|
1054
|
+
return core.suspend(() => {
|
|
1055
|
+
if (MutableRef.get(this.shutdownFlag)) {
|
|
1056
|
+
return core.interrupt
|
|
1057
|
+
}
|
|
1058
|
+
let replay: Chunk.Chunk<A> | undefined = undefined
|
|
1059
|
+
if (this.replayWindow.remaining >= max) {
|
|
1060
|
+
const as = this.replayWindow.takeN(max)
|
|
1061
|
+
return core.succeed(as)
|
|
1062
|
+
} else if (this.replayWindow.remaining > 0) {
|
|
1063
|
+
replay = this.replayWindow.takeAll()
|
|
1064
|
+
max = max - replay.length
|
|
1065
|
+
}
|
|
1066
|
+
const as = MutableQueue.isEmpty(this.pollers)
|
|
1067
|
+
? unsafePollN(this.subscription, max)
|
|
1068
|
+
: Chunk.empty()
|
|
1069
|
+
this.strategy.unsafeOnPubSubEmptySpace(this.pubsub, this.subscribers)
|
|
1070
|
+
return replay ? core.succeed(Chunk.appendAll(replay, as)) : core.succeed(as)
|
|
1071
|
+
})
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
takeBetween(min: number, max: number): Effect.Effect<Chunk.Chunk<A>> {
|
|
1075
|
+
return core.suspend(() => takeRemainderLoop(this, min, max, Chunk.empty()))
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
/** @internal */
|
|
1080
|
+
const takeRemainderLoop = <A>(
|
|
1081
|
+
self: Queue.Dequeue<A>,
|
|
1082
|
+
min: number,
|
|
1083
|
+
max: number,
|
|
1084
|
+
acc: Chunk.Chunk<A>
|
|
1085
|
+
): Effect.Effect<Chunk.Chunk<A>> => {
|
|
1086
|
+
if (max < min) {
|
|
1087
|
+
return core.succeed(acc)
|
|
1088
|
+
}
|
|
1089
|
+
return pipe(
|
|
1090
|
+
self.takeUpTo(max),
|
|
1091
|
+
core.flatMap((bs) => {
|
|
1092
|
+
const remaining = min - bs.length
|
|
1093
|
+
if (remaining === 1) {
|
|
1094
|
+
return pipe(self.take, core.map((b) => pipe(acc, Chunk.appendAll(bs), Chunk.append(b))))
|
|
1095
|
+
}
|
|
1096
|
+
if (remaining > 1) {
|
|
1097
|
+
return pipe(
|
|
1098
|
+
self.take,
|
|
1099
|
+
core.flatMap((b) =>
|
|
1100
|
+
takeRemainderLoop(
|
|
1101
|
+
self,
|
|
1102
|
+
remaining - 1,
|
|
1103
|
+
max - bs.length - 1,
|
|
1104
|
+
pipe(acc, Chunk.appendAll(bs), Chunk.append(b))
|
|
1105
|
+
)
|
|
1106
|
+
)
|
|
1107
|
+
)
|
|
1108
|
+
}
|
|
1109
|
+
return core.succeed(pipe(acc, Chunk.appendAll(bs)))
|
|
1110
|
+
})
|
|
1111
|
+
)
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/** @internal */
|
|
1115
|
+
class PubSubImpl<in out A> implements PubSub.PubSub<A> {
|
|
1116
|
+
readonly [queue.EnqueueTypeId] = queue.enqueueVariance
|
|
1117
|
+
readonly [queue.DequeueTypeId] = queue.dequeueVariance
|
|
1118
|
+
|
|
1119
|
+
constructor(
|
|
1120
|
+
readonly pubsub: AtomicPubSub<A>,
|
|
1121
|
+
readonly subscribers: Subscribers<A>,
|
|
1122
|
+
readonly scope: Scope.Scope.Closeable,
|
|
1123
|
+
readonly shutdownHook: Deferred.Deferred<void>,
|
|
1124
|
+
readonly shutdownFlag: MutableRef.MutableRef<boolean>,
|
|
1125
|
+
readonly strategy: PubSubStrategy<A>
|
|
1126
|
+
) {}
|
|
1127
|
+
|
|
1128
|
+
capacity(): number {
|
|
1129
|
+
return this.pubsub.capacity
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
get size(): Effect.Effect<number> {
|
|
1133
|
+
return core.suspend(() =>
|
|
1134
|
+
MutableRef.get(this.shutdownFlag) ?
|
|
1135
|
+
core.interrupt :
|
|
1136
|
+
core.sync(() => this.pubsub.size())
|
|
1137
|
+
)
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
unsafeSize(): Option.Option<number> {
|
|
1141
|
+
if (MutableRef.get(this.shutdownFlag)) {
|
|
1142
|
+
return Option.none()
|
|
1143
|
+
}
|
|
1144
|
+
return Option.some(this.pubsub.size())
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
get isFull(): Effect.Effect<boolean> {
|
|
1148
|
+
return core.map(this.size, (size) => size === this.capacity())
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
get isEmpty(): Effect.Effect<boolean> {
|
|
1152
|
+
return core.map(this.size, (size) => size === 0)
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
get awaitShutdown(): Effect.Effect<void> {
|
|
1156
|
+
return core.deferredAwait(this.shutdownHook)
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
get isShutdown(): Effect.Effect<boolean> {
|
|
1160
|
+
return core.sync(() => MutableRef.get(this.shutdownFlag))
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
get shutdown(): Effect.Effect<void> {
|
|
1164
|
+
return core.uninterruptible(core.withFiberRuntime((state) => {
|
|
1165
|
+
pipe(this.shutdownFlag, MutableRef.set(true))
|
|
1166
|
+
return pipe(
|
|
1167
|
+
this.scope.close(core.exitInterrupt(state.id())),
|
|
1168
|
+
core.zipRight(this.strategy.shutdown),
|
|
1169
|
+
core.whenEffect(core.deferredSucceed(this.shutdownHook, void 0)),
|
|
1170
|
+
core.asVoid
|
|
1171
|
+
)
|
|
1172
|
+
}))
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
publish(value: A): Effect.Effect<boolean> {
|
|
1176
|
+
return core.suspend(() => {
|
|
1177
|
+
if (MutableRef.get(this.shutdownFlag)) {
|
|
1178
|
+
return core.interrupt
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
if (this.pubsub.publish(value)) {
|
|
1182
|
+
this.strategy.unsafeCompleteSubscribers(this.pubsub, this.subscribers)
|
|
1183
|
+
return core.succeed(true)
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
return this.strategy.handleSurplus(
|
|
1187
|
+
this.pubsub,
|
|
1188
|
+
this.subscribers,
|
|
1189
|
+
Chunk.of(value),
|
|
1190
|
+
this.shutdownFlag
|
|
1191
|
+
)
|
|
1192
|
+
})
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
isActive(): boolean {
|
|
1196
|
+
return !MutableRef.get(this.shutdownFlag)
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
unsafeOffer(value: A): boolean {
|
|
1200
|
+
if (MutableRef.get(this.shutdownFlag)) {
|
|
1201
|
+
return false
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
if ((this.pubsub as AtomicPubSub<unknown>).publish(value)) {
|
|
1205
|
+
this.strategy.unsafeCompleteSubscribers(this.pubsub, this.subscribers)
|
|
1206
|
+
return true
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
return false
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
publishAll(elements: Iterable<A>): Effect.Effect<boolean> {
|
|
1213
|
+
return core.suspend(() => {
|
|
1214
|
+
if (MutableRef.get(this.shutdownFlag)) {
|
|
1215
|
+
return core.interrupt
|
|
1216
|
+
}
|
|
1217
|
+
const surplus = unsafePublishAll(this.pubsub, elements)
|
|
1218
|
+
this.strategy.unsafeCompleteSubscribers(this.pubsub, this.subscribers)
|
|
1219
|
+
if (Chunk.isEmpty(surplus)) {
|
|
1220
|
+
return core.succeed(true)
|
|
1221
|
+
}
|
|
1222
|
+
return this.strategy.handleSurplus(
|
|
1223
|
+
this.pubsub,
|
|
1224
|
+
this.subscribers,
|
|
1225
|
+
surplus,
|
|
1226
|
+
this.shutdownFlag
|
|
1227
|
+
)
|
|
1228
|
+
})
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
get subscribe(): Effect.Effect<Queue.Dequeue<A>, never, Scope.Scope> {
|
|
1232
|
+
const acquire = core.tap(
|
|
1233
|
+
fiberRuntime.all([
|
|
1234
|
+
this.scope.fork(executionStrategy.sequential),
|
|
1235
|
+
makeSubscription(this.pubsub, this.subscribers, this.strategy)
|
|
1236
|
+
]),
|
|
1237
|
+
(tuple) => tuple[0].addFinalizer(() => tuple[1].shutdown)
|
|
1238
|
+
)
|
|
1239
|
+
return core.map(
|
|
1240
|
+
fiberRuntime.acquireRelease(acquire, (tuple, exit) => tuple[0].close(exit)),
|
|
1241
|
+
(tuple) => tuple[1]
|
|
1242
|
+
)
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
offer(value: A): Effect.Effect<boolean> {
|
|
1246
|
+
return this.publish(value)
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
offerAll(elements: Iterable<A>): Effect.Effect<boolean> {
|
|
1250
|
+
return this.publishAll(elements)
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
pipe() {
|
|
1254
|
+
return pipeArguments(this, arguments)
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/** @internal */
|
|
1259
|
+
export const makePubSub = <A>(
|
|
1260
|
+
pubsub: AtomicPubSub<A>,
|
|
1261
|
+
strategy: PubSubStrategy<A>
|
|
1262
|
+
): Effect.Effect<PubSub.PubSub<A>> =>
|
|
1263
|
+
core.flatMap(
|
|
1264
|
+
fiberRuntime.scopeMake(),
|
|
1265
|
+
(scope) =>
|
|
1266
|
+
core.map(core.deferredMake<void>(), (deferred) =>
|
|
1267
|
+
unsafeMakePubSub(
|
|
1268
|
+
pubsub,
|
|
1269
|
+
new Map(),
|
|
1270
|
+
scope,
|
|
1271
|
+
deferred,
|
|
1272
|
+
MutableRef.make(false),
|
|
1273
|
+
strategy
|
|
1274
|
+
))
|
|
1275
|
+
)
|
|
1276
|
+
|
|
1277
|
+
/** @internal */
|
|
1278
|
+
export const unsafeMakePubSub = <A>(
|
|
1279
|
+
pubsub: AtomicPubSub<A>,
|
|
1280
|
+
subscribers: Subscribers<A>,
|
|
1281
|
+
scope: Scope.Scope.Closeable,
|
|
1282
|
+
shutdownHook: Deferred.Deferred<void>,
|
|
1283
|
+
shutdownFlag: MutableRef.MutableRef<boolean>,
|
|
1284
|
+
strategy: PubSubStrategy<A>
|
|
1285
|
+
): PubSub.PubSub<A> => new PubSubImpl(pubsub, subscribers, scope, shutdownHook, shutdownFlag, strategy)
|
|
1286
|
+
|
|
1287
|
+
/** @internal */
|
|
1288
|
+
const ensureCapacity = (capacity: number): void => {
|
|
1289
|
+
if (capacity <= 0) {
|
|
1290
|
+
throw new core.InvalidPubSubCapacityException(`Cannot construct PubSub with capacity of ${capacity}`)
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/** @internal */
|
|
1295
|
+
const unsafeCompleteDeferred = <A>(deferred: Deferred.Deferred<A>, a: A): void => {
|
|
1296
|
+
core.deferredUnsafeDone(deferred, core.succeed(a))
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
/** @internal */
|
|
1300
|
+
const unsafeOfferAll = <A>(queue: MutableQueue.MutableQueue<A>, as: Iterable<A>): Chunk.Chunk<A> => {
|
|
1301
|
+
return pipe(queue, MutableQueue.offerAll(as))
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
/** @internal */
|
|
1305
|
+
const unsafePollAllQueue = <A>(queue: MutableQueue.MutableQueue<A>): Chunk.Chunk<A> => {
|
|
1306
|
+
return pipe(queue, MutableQueue.pollUpTo(Number.POSITIVE_INFINITY))
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/** @internal */
|
|
1310
|
+
const unsafePollAllSubscription = <A>(subscription: Subscription<A>): Chunk.Chunk<A> => {
|
|
1311
|
+
return subscription.pollUpTo(Number.POSITIVE_INFINITY)
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/** @internal */
|
|
1315
|
+
const unsafePollN = <A>(subscription: Subscription<A>, max: number): Chunk.Chunk<A> => {
|
|
1316
|
+
return subscription.pollUpTo(max)
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/** @internal */
|
|
1320
|
+
const unsafePublishAll = <A>(pubsub: AtomicPubSub<A>, as: Iterable<A>): Chunk.Chunk<A> => {
|
|
1321
|
+
return pubsub.publishAll(as)
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/** @internal */
|
|
1325
|
+
const unsafeRemove = <A>(queue: MutableQueue.MutableQueue<A>, value: A): void => {
|
|
1326
|
+
unsafeOfferAll(
|
|
1327
|
+
queue,
|
|
1328
|
+
pipe(unsafePollAllQueue(queue), Chunk.filter((elem) => elem !== value))
|
|
1329
|
+
)
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// -----------------------------------------------------------------------------
|
|
1333
|
+
// PubSub.Strategy
|
|
1334
|
+
// -----------------------------------------------------------------------------
|
|
1335
|
+
|
|
1336
|
+
/**
|
|
1337
|
+
* A `PubSubStrategy<A>` describes the protocol for how publishers and subscribers
|
|
1338
|
+
* will communicate with each other through the `PubSub`.
|
|
1339
|
+
*
|
|
1340
|
+
* @internal
|
|
1341
|
+
*/
|
|
1342
|
+
export interface PubSubStrategy<in out A> {
|
|
1343
|
+
/**
|
|
1344
|
+
* Describes any finalization logic associated with this strategy.
|
|
1345
|
+
*/
|
|
1346
|
+
readonly shutdown: Effect.Effect<void>
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Describes how publishers should signal to subscribers that they are
|
|
1350
|
+
* waiting for space to become available in the `PubSub`.
|
|
1351
|
+
*/
|
|
1352
|
+
handleSurplus(
|
|
1353
|
+
pubsub: AtomicPubSub<A>,
|
|
1354
|
+
subscribers: Subscribers<A>,
|
|
1355
|
+
elements: Iterable<A>,
|
|
1356
|
+
isShutdown: MutableRef.MutableRef<boolean>
|
|
1357
|
+
): Effect.Effect<boolean>
|
|
1358
|
+
|
|
1359
|
+
/**
|
|
1360
|
+
* Describes how subscribers should signal to publishers waiting for space
|
|
1361
|
+
* to become available in the `PubSub` that space may be available.
|
|
1362
|
+
*/
|
|
1363
|
+
unsafeOnPubSubEmptySpace(
|
|
1364
|
+
pubsub: AtomicPubSub<A>,
|
|
1365
|
+
subscribers: Subscribers<A>
|
|
1366
|
+
): void
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* Describes how subscribers waiting for additional values from the `PubSub`
|
|
1370
|
+
* should take those values and signal to publishers that they are no
|
|
1371
|
+
* longer waiting for additional values.
|
|
1372
|
+
*/
|
|
1373
|
+
unsafeCompletePollers(
|
|
1374
|
+
pubsub: AtomicPubSub<A>,
|
|
1375
|
+
subscribers: Subscribers<A>,
|
|
1376
|
+
subscription: Subscription<A>,
|
|
1377
|
+
pollers: MutableQueue.MutableQueue<Deferred.Deferred<A>>
|
|
1378
|
+
): void
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
* Describes how publishers should signal to subscribers waiting for
|
|
1382
|
+
* additional values from the `PubSub` that new values are available.
|
|
1383
|
+
*/
|
|
1384
|
+
unsafeCompleteSubscribers(
|
|
1385
|
+
pubsub: AtomicPubSub<A>,
|
|
1386
|
+
subscribers: Subscribers<A>
|
|
1387
|
+
): void
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
/**
|
|
1391
|
+
* A strategy that applies back pressure to publishers when the `PubSub` is at
|
|
1392
|
+
* capacity. This guarantees that all subscribers will receive all messages
|
|
1393
|
+
* published to the `PubSub` while they are subscribed. However, it creates the
|
|
1394
|
+
* risk that a slow subscriber will slow down the rate at which messages
|
|
1395
|
+
* are published and received by other subscribers.
|
|
1396
|
+
*
|
|
1397
|
+
* @internal
|
|
1398
|
+
*/
|
|
1399
|
+
class BackPressureStrategy<in out A> implements PubSubStrategy<A> {
|
|
1400
|
+
publishers: MutableQueue.MutableQueue<
|
|
1401
|
+
readonly [A, Deferred.Deferred<boolean>, boolean]
|
|
1402
|
+
> = MutableQueue.unbounded()
|
|
1403
|
+
|
|
1404
|
+
get shutdown(): Effect.Effect<void> {
|
|
1405
|
+
return core.flatMap(core.fiberId, (fiberId) =>
|
|
1406
|
+
core.flatMap(
|
|
1407
|
+
core.sync(() => unsafePollAllQueue(this.publishers)),
|
|
1408
|
+
(publishers) =>
|
|
1409
|
+
fiberRuntime.forEachConcurrentDiscard(
|
|
1410
|
+
publishers,
|
|
1411
|
+
([_, deferred, last]) =>
|
|
1412
|
+
last ?
|
|
1413
|
+
pipe(core.deferredInterruptWith(deferred, fiberId), core.asVoid) :
|
|
1414
|
+
core.void,
|
|
1415
|
+
false,
|
|
1416
|
+
false
|
|
1417
|
+
)
|
|
1418
|
+
))
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
handleSurplus(
|
|
1422
|
+
pubsub: AtomicPubSub<A>,
|
|
1423
|
+
subscribers: Subscribers<A>,
|
|
1424
|
+
elements: Iterable<A>,
|
|
1425
|
+
isShutdown: MutableRef.MutableRef<boolean>
|
|
1426
|
+
): Effect.Effect<boolean> {
|
|
1427
|
+
return core.withFiberRuntime((state) => {
|
|
1428
|
+
const deferred = core.deferredUnsafeMake<boolean>(state.id())
|
|
1429
|
+
return pipe(
|
|
1430
|
+
core.suspend(() => {
|
|
1431
|
+
this.unsafeOffer(elements, deferred)
|
|
1432
|
+
this.unsafeOnPubSubEmptySpace(pubsub, subscribers)
|
|
1433
|
+
this.unsafeCompleteSubscribers(pubsub, subscribers)
|
|
1434
|
+
return MutableRef.get(isShutdown) ?
|
|
1435
|
+
core.interrupt :
|
|
1436
|
+
core.deferredAwait(deferred)
|
|
1437
|
+
}),
|
|
1438
|
+
core.onInterrupt(() => core.sync(() => this.unsafeRemove(deferred)))
|
|
1439
|
+
)
|
|
1440
|
+
})
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
unsafeOnPubSubEmptySpace(
|
|
1444
|
+
pubsub: AtomicPubSub<A>,
|
|
1445
|
+
subscribers: Subscribers<A>
|
|
1446
|
+
): void {
|
|
1447
|
+
let keepPolling = true
|
|
1448
|
+
while (keepPolling && !pubsub.isFull()) {
|
|
1449
|
+
const publisher = pipe(this.publishers, MutableQueue.poll(MutableQueue.EmptyMutableQueue))
|
|
1450
|
+
if (publisher === MutableQueue.EmptyMutableQueue) {
|
|
1451
|
+
keepPolling = false
|
|
1452
|
+
} else {
|
|
1453
|
+
const published = pubsub.publish(publisher[0])
|
|
1454
|
+
if (published && publisher[2]) {
|
|
1455
|
+
unsafeCompleteDeferred(publisher[1], true)
|
|
1456
|
+
} else if (!published) {
|
|
1457
|
+
unsafeOfferAll(
|
|
1458
|
+
this.publishers,
|
|
1459
|
+
pipe(unsafePollAllQueue(this.publishers), Chunk.prepend(publisher))
|
|
1460
|
+
)
|
|
1461
|
+
}
|
|
1462
|
+
this.unsafeCompleteSubscribers(pubsub, subscribers)
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
unsafeCompletePollers(
|
|
1468
|
+
pubsub: AtomicPubSub<A>,
|
|
1469
|
+
subscribers: Subscribers<A>,
|
|
1470
|
+
subscription: Subscription<A>,
|
|
1471
|
+
pollers: MutableQueue.MutableQueue<Deferred.Deferred<A>>
|
|
1472
|
+
): void {
|
|
1473
|
+
return unsafeStrategyCompletePollers(this, pubsub, subscribers, subscription, pollers)
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
unsafeCompleteSubscribers(pubsub: AtomicPubSub<A>, subscribers: Subscribers<A>): void {
|
|
1477
|
+
return unsafeStrategyCompleteSubscribers(this, pubsub, subscribers)
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
private unsafeOffer(elements: Iterable<A>, deferred: Deferred.Deferred<boolean>): void {
|
|
1481
|
+
const iterator = elements[Symbol.iterator]()
|
|
1482
|
+
let next: IteratorResult<A> = iterator.next()
|
|
1483
|
+
if (!next.done) {
|
|
1484
|
+
// eslint-disable-next-line no-constant-condition
|
|
1485
|
+
while (1) {
|
|
1486
|
+
const value = next.value
|
|
1487
|
+
next = iterator.next()
|
|
1488
|
+
if (next.done) {
|
|
1489
|
+
pipe(
|
|
1490
|
+
this.publishers,
|
|
1491
|
+
MutableQueue.offer([value, deferred, true as boolean] as const)
|
|
1492
|
+
)
|
|
1493
|
+
break
|
|
1494
|
+
}
|
|
1495
|
+
pipe(
|
|
1496
|
+
this.publishers,
|
|
1497
|
+
MutableQueue.offer([value, deferred, false as boolean] as const)
|
|
1498
|
+
)
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
unsafeRemove(deferred: Deferred.Deferred<boolean>): void {
|
|
1504
|
+
unsafeOfferAll(
|
|
1505
|
+
this.publishers,
|
|
1506
|
+
pipe(unsafePollAllQueue(this.publishers), Chunk.filter(([_, a]) => a !== deferred))
|
|
1507
|
+
)
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* A strategy that drops new messages when the `PubSub` is at capacity. This
|
|
1513
|
+
* guarantees that a slow subscriber will not slow down the rate at which
|
|
1514
|
+
* messages are published. However, it creates the risk that a slow
|
|
1515
|
+
* subscriber will slow down the rate at which messages are received by
|
|
1516
|
+
* other subscribers and that subscribers may not receive all messages
|
|
1517
|
+
* published to the `PubSub` while they are subscribed.
|
|
1518
|
+
*
|
|
1519
|
+
* @internal
|
|
1520
|
+
*/
|
|
1521
|
+
export class DroppingStrategy<in out A> implements PubSubStrategy<A> {
|
|
1522
|
+
get shutdown(): Effect.Effect<void> {
|
|
1523
|
+
return core.void
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
handleSurplus(
|
|
1527
|
+
_pubsub: AtomicPubSub<A>,
|
|
1528
|
+
_subscribers: Subscribers<A>,
|
|
1529
|
+
_elements: Iterable<A>,
|
|
1530
|
+
_isShutdown: MutableRef.MutableRef<boolean>
|
|
1531
|
+
): Effect.Effect<boolean> {
|
|
1532
|
+
return core.succeed(false)
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
unsafeOnPubSubEmptySpace(
|
|
1536
|
+
_pubsub: AtomicPubSub<A>,
|
|
1537
|
+
_subscribers: Subscribers<A>
|
|
1538
|
+
): void {
|
|
1539
|
+
//
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
unsafeCompletePollers(
|
|
1543
|
+
pubsub: AtomicPubSub<A>,
|
|
1544
|
+
subscribers: Subscribers<A>,
|
|
1545
|
+
subscription: Subscription<A>,
|
|
1546
|
+
pollers: MutableQueue.MutableQueue<Deferred.Deferred<A>>
|
|
1547
|
+
): void {
|
|
1548
|
+
return unsafeStrategyCompletePollers(this, pubsub, subscribers, subscription, pollers)
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
unsafeCompleteSubscribers(pubsub: AtomicPubSub<A>, subscribers: Subscribers<A>): void {
|
|
1552
|
+
return unsafeStrategyCompleteSubscribers(this, pubsub, subscribers)
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
/**
|
|
1557
|
+
* A strategy that adds new messages and drops old messages when the `PubSub` is
|
|
1558
|
+
* at capacity. This guarantees that a slow subscriber will not slow down
|
|
1559
|
+
* the rate at which messages are published and received by other
|
|
1560
|
+
* subscribers. However, it creates the risk that a slow subscriber will
|
|
1561
|
+
* not receive some messages published to the `PubSub` while it is subscribed.
|
|
1562
|
+
*
|
|
1563
|
+
* @internal
|
|
1564
|
+
*/
|
|
1565
|
+
export class SlidingStrategy<in out A> implements PubSubStrategy<A> {
|
|
1566
|
+
get shutdown(): Effect.Effect<void> {
|
|
1567
|
+
return core.void
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
handleSurplus(
|
|
1571
|
+
pubsub: AtomicPubSub<A>,
|
|
1572
|
+
subscribers: Subscribers<A>,
|
|
1573
|
+
elements: Iterable<A>,
|
|
1574
|
+
_isShutdown: MutableRef.MutableRef<boolean>
|
|
1575
|
+
): Effect.Effect<boolean> {
|
|
1576
|
+
return core.sync(() => {
|
|
1577
|
+
this.unsafeSlidingPublish(pubsub, elements)
|
|
1578
|
+
this.unsafeCompleteSubscribers(pubsub, subscribers)
|
|
1579
|
+
return true
|
|
1580
|
+
})
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
unsafeOnPubSubEmptySpace(
|
|
1584
|
+
_pubsub: AtomicPubSub<A>,
|
|
1585
|
+
_subscribers: Subscribers<A>
|
|
1586
|
+
): void {
|
|
1587
|
+
//
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
unsafeCompletePollers(
|
|
1591
|
+
pubsub: AtomicPubSub<A>,
|
|
1592
|
+
subscribers: Subscribers<A>,
|
|
1593
|
+
subscription: Subscription<A>,
|
|
1594
|
+
pollers: MutableQueue.MutableQueue<Deferred.Deferred<A>>
|
|
1595
|
+
): void {
|
|
1596
|
+
return unsafeStrategyCompletePollers(this, pubsub, subscribers, subscription, pollers)
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
unsafeCompleteSubscribers(pubsub: AtomicPubSub<A>, subscribers: Subscribers<A>): void {
|
|
1600
|
+
return unsafeStrategyCompleteSubscribers(this, pubsub, subscribers)
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
unsafeSlidingPublish(pubsub: AtomicPubSub<A>, elements: Iterable<A>): void {
|
|
1604
|
+
const it = elements[Symbol.iterator]()
|
|
1605
|
+
let next = it.next()
|
|
1606
|
+
if (!next.done && pubsub.capacity > 0) {
|
|
1607
|
+
let a = next.value
|
|
1608
|
+
let loop = true
|
|
1609
|
+
while (loop) {
|
|
1610
|
+
pubsub.slide()
|
|
1611
|
+
const pub = pubsub.publish(a)
|
|
1612
|
+
if (pub && (next = it.next()) && !next.done) {
|
|
1613
|
+
a = next.value
|
|
1614
|
+
} else if (pub) {
|
|
1615
|
+
loop = false
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
/** @internal */
|
|
1623
|
+
const unsafeStrategyCompletePollers = <A>(
|
|
1624
|
+
strategy: PubSubStrategy<A>,
|
|
1625
|
+
pubsub: AtomicPubSub<A>,
|
|
1626
|
+
subscribers: Subscribers<A>,
|
|
1627
|
+
subscription: Subscription<A>,
|
|
1628
|
+
pollers: MutableQueue.MutableQueue<Deferred.Deferred<A>>
|
|
1629
|
+
): void => {
|
|
1630
|
+
let keepPolling = true
|
|
1631
|
+
while (keepPolling && !subscription.isEmpty()) {
|
|
1632
|
+
const poller = pipe(pollers, MutableQueue.poll(MutableQueue.EmptyMutableQueue))
|
|
1633
|
+
if (poller === MutableQueue.EmptyMutableQueue) {
|
|
1634
|
+
pipe(subscribers, removeSubscribers(subscription, pollers))
|
|
1635
|
+
if (MutableQueue.isEmpty(pollers)) {
|
|
1636
|
+
keepPolling = false
|
|
1637
|
+
} else {
|
|
1638
|
+
pipe(subscribers, addSubscribers(subscription, pollers))
|
|
1639
|
+
}
|
|
1640
|
+
} else {
|
|
1641
|
+
const pollResult = subscription.poll(MutableQueue.EmptyMutableQueue)
|
|
1642
|
+
if (pollResult === MutableQueue.EmptyMutableQueue) {
|
|
1643
|
+
unsafeOfferAll(pollers, pipe(unsafePollAllQueue(pollers), Chunk.prepend(poller)))
|
|
1644
|
+
} else {
|
|
1645
|
+
unsafeCompleteDeferred(poller, pollResult)
|
|
1646
|
+
strategy.unsafeOnPubSubEmptySpace(pubsub, subscribers)
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
/** @internal */
|
|
1653
|
+
const unsafeStrategyCompleteSubscribers = <A>(
|
|
1654
|
+
strategy: PubSubStrategy<A>,
|
|
1655
|
+
pubsub: AtomicPubSub<A>,
|
|
1656
|
+
subscribers: Subscribers<A>
|
|
1657
|
+
): void => {
|
|
1658
|
+
for (
|
|
1659
|
+
const [subscription, pollersSet] of subscribers
|
|
1660
|
+
) {
|
|
1661
|
+
for (const pollers of pollersSet) {
|
|
1662
|
+
strategy.unsafeCompletePollers(pubsub, subscribers, subscription, pollers)
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
interface ReplayNode<A> {
|
|
1668
|
+
value: A | AbsentValue
|
|
1669
|
+
next: ReplayNode<A> | null
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
class ReplayBuffer<A> {
|
|
1673
|
+
constructor(readonly capacity: number) {}
|
|
1674
|
+
|
|
1675
|
+
head: ReplayNode<A> = { value: AbsentValue, next: null }
|
|
1676
|
+
tail: ReplayNode<A> = this.head
|
|
1677
|
+
size = 0
|
|
1678
|
+
index = 0
|
|
1679
|
+
|
|
1680
|
+
slide() {
|
|
1681
|
+
this.index++
|
|
1682
|
+
}
|
|
1683
|
+
offer(a: A): void {
|
|
1684
|
+
this.tail.value = a
|
|
1685
|
+
this.tail.next = {
|
|
1686
|
+
value: AbsentValue,
|
|
1687
|
+
next: null
|
|
1688
|
+
}
|
|
1689
|
+
this.tail = this.tail.next
|
|
1690
|
+
if (this.size === this.capacity) {
|
|
1691
|
+
this.head = this.head.next!
|
|
1692
|
+
} else {
|
|
1693
|
+
this.size += 1
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
offerAll(as: Iterable<A>): void {
|
|
1697
|
+
for (const a of as) {
|
|
1698
|
+
this.offer(a)
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
interface ReplayWindow<A> {
|
|
1704
|
+
take(): A | undefined
|
|
1705
|
+
takeN(n: number): Chunk.Chunk<A>
|
|
1706
|
+
takeAll(): Chunk.Chunk<A>
|
|
1707
|
+
readonly remaining: number
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
class ReplayWindowImpl<A> implements ReplayWindow<A> {
|
|
1711
|
+
head: ReplayNode<A>
|
|
1712
|
+
index: number
|
|
1713
|
+
remaining: number
|
|
1714
|
+
constructor(readonly buffer: ReplayBuffer<A>) {
|
|
1715
|
+
this.index = buffer.index
|
|
1716
|
+
this.remaining = buffer.size
|
|
1717
|
+
this.head = buffer.head
|
|
1718
|
+
}
|
|
1719
|
+
fastForward() {
|
|
1720
|
+
while (this.index < this.buffer.index) {
|
|
1721
|
+
this.head = this.head.next!
|
|
1722
|
+
this.index++
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
take(): A | undefined {
|
|
1726
|
+
if (this.remaining === 0) {
|
|
1727
|
+
return undefined
|
|
1728
|
+
} else if (this.index < this.buffer.index) {
|
|
1729
|
+
this.fastForward()
|
|
1730
|
+
}
|
|
1731
|
+
this.remaining--
|
|
1732
|
+
const value = this.head.value
|
|
1733
|
+
this.head = this.head.next!
|
|
1734
|
+
return value as A
|
|
1735
|
+
}
|
|
1736
|
+
takeN(n: number): Chunk.Chunk<A> {
|
|
1737
|
+
if (this.remaining === 0) {
|
|
1738
|
+
return Chunk.empty()
|
|
1739
|
+
} else if (this.index < this.buffer.index) {
|
|
1740
|
+
this.fastForward()
|
|
1741
|
+
}
|
|
1742
|
+
const len = Math.min(n, this.remaining)
|
|
1743
|
+
const items = new Array(len)
|
|
1744
|
+
for (let i = 0; i < len; i++) {
|
|
1745
|
+
const value = this.head.value as A
|
|
1746
|
+
this.head = this.head.next!
|
|
1747
|
+
items[i] = value
|
|
1748
|
+
}
|
|
1749
|
+
this.remaining -= len
|
|
1750
|
+
return Chunk.unsafeFromArray(items)
|
|
1751
|
+
}
|
|
1752
|
+
takeAll(): Chunk.Chunk<A> {
|
|
1753
|
+
return this.takeN(this.remaining)
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
const emptyReplayWindow: ReplayWindow<never> = {
|
|
1758
|
+
remaining: 0,
|
|
1759
|
+
take: () => undefined,
|
|
1760
|
+
takeN: () => Chunk.empty(),
|
|
1761
|
+
takeAll: () => Chunk.empty()
|
|
1762
|
+
}
|