Skip to content

Commit 232c353

Browse files
authoredApr 10, 2024··
simplify scope internals (#2500)
1 parent 217147e commit 232c353

File tree

3 files changed

+108
-214
lines changed

3 files changed

+108
-214
lines changed
 

‎.changeset/slimy-meals-impress.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"effect": patch
3+
---
4+
5+
simplify scope internals

‎packages/effect/src/internal/core.ts

-137
Original file line numberDiff line numberDiff line change
@@ -2086,143 +2086,6 @@ export const scopeFork = (
20862086
strategy: ExecutionStrategy.ExecutionStrategy
20872087
): Effect.Effect<Scope.Scope.Closeable> => self.fork(strategy)
20882088

2089-
// -----------------------------------------------------------------------------
2090-
// ReleaseMap
2091-
// -----------------------------------------------------------------------------
2092-
2093-
/** @internal */
2094-
export type ReleaseMapState = {
2095-
_tag: "Exited"
2096-
nextKey: number
2097-
exit: Exit.Exit<unknown, unknown>
2098-
update: (finalizer: Scope.Scope.Finalizer) => Scope.Scope.Finalizer
2099-
} | {
2100-
_tag: "Running"
2101-
nextKey: number
2102-
finalizers: Map<number, Scope.Scope.Finalizer>
2103-
update: (finalizer: Scope.Scope.Finalizer) => Scope.Scope.Finalizer
2104-
}
2105-
2106-
/** @internal */
2107-
export interface ReleaseMap {
2108-
state: ReleaseMapState // mutable by design
2109-
}
2110-
2111-
/* @internal */
2112-
export const releaseMapAdd = dual<
2113-
(finalizer: Scope.Scope.Finalizer) => (self: ReleaseMap) => Effect.Effect<Scope.Scope.Finalizer>,
2114-
(self: ReleaseMap, finalizer: Scope.Scope.Finalizer) => Effect.Effect<Scope.Scope.Finalizer>
2115-
>(2, (self, finalizer) =>
2116-
map(
2117-
releaseMapAddIfOpen(self, finalizer),
2118-
Option.match({
2119-
onNone: (): Scope.Scope.Finalizer => () => unit,
2120-
onSome: (key): Scope.Scope.Finalizer => (exit) => releaseMapRelease(key, exit)(self)
2121-
})
2122-
))
2123-
2124-
/* @internal */
2125-
export const releaseMapRelease = dual<
2126-
(key: number, exit: Exit.Exit<unknown, unknown>) => (self: ReleaseMap) => Effect.Effect<void>,
2127-
(self: ReleaseMap, key: number, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<void>
2128-
>(3, (self, key, exit) =>
2129-
suspend(() => {
2130-
switch (self.state._tag) {
2131-
case "Exited": {
2132-
return unit
2133-
}
2134-
case "Running": {
2135-
const finalizer = self.state.finalizers.get(key)
2136-
self.state.finalizers.delete(key)
2137-
if (finalizer != null) {
2138-
return self.state.update(finalizer)(exit)
2139-
}
2140-
return unit
2141-
}
2142-
}
2143-
}))
2144-
2145-
/* @internal */
2146-
export const releaseMapAddIfOpen = dual<
2147-
(finalizer: Scope.Scope.Finalizer) => (self: ReleaseMap) => Effect.Effect<Option.Option<number>>,
2148-
(self: ReleaseMap, finalizer: Scope.Scope.Finalizer) => Effect.Effect<Option.Option<number>>
2149-
>(2, (self, finalizer) =>
2150-
suspend(() => {
2151-
switch (self.state._tag) {
2152-
case "Exited": {
2153-
self.state.nextKey += 1
2154-
return as(finalizer(self.state.exit), Option.none())
2155-
}
2156-
case "Running": {
2157-
const key = self.state.nextKey
2158-
self.state.finalizers.set(key, finalizer)
2159-
self.state.nextKey += 1
2160-
return succeed(Option.some(key))
2161-
}
2162-
}
2163-
}))
2164-
2165-
/* @internal */
2166-
export const releaseMapGet = dual<
2167-
(key: number) => (self: ReleaseMap) => Effect.Effect<Option.Option<Scope.Scope.Finalizer>>,
2168-
(self: ReleaseMap, key: number) => Effect.Effect<Option.Option<Scope.Scope.Finalizer>>
2169-
>(
2170-
2,
2171-
(self, key) =>
2172-
sync((): Option.Option<Scope.Scope.Finalizer> =>
2173-
self.state._tag === "Running" ? Option.fromNullable(self.state.finalizers.get(key)) : Option.none()
2174-
)
2175-
)
2176-
2177-
/* @internal */
2178-
export const releaseMapReplace = dual<
2179-
(
2180-
key: number,
2181-
finalizer: Scope.Scope.Finalizer
2182-
) => (self: ReleaseMap) => Effect.Effect<Option.Option<Scope.Scope.Finalizer>>,
2183-
(
2184-
self: ReleaseMap,
2185-
key: number,
2186-
finalizer: Scope.Scope.Finalizer
2187-
) => Effect.Effect<Option.Option<Scope.Scope.Finalizer>>
2188-
>(3, (self, key, finalizer) =>
2189-
suspend(() => {
2190-
switch (self.state._tag) {
2191-
case "Exited": {
2192-
return as(finalizer(self.state.exit), Option.none())
2193-
}
2194-
case "Running": {
2195-
const fin = Option.fromNullable(self.state.finalizers.get(key))
2196-
self.state.finalizers.set(key, finalizer)
2197-
return succeed(fin)
2198-
}
2199-
}
2200-
}))
2201-
2202-
/* @internal */
2203-
export const releaseMapRemove = dual<
2204-
(key: number) => (self: ReleaseMap) => Effect.Effect<Option.Option<Scope.Scope.Finalizer>>,
2205-
(self: ReleaseMap, key: number) => Effect.Effect<Option.Option<Scope.Scope.Finalizer>>
2206-
>(2, (self, key) =>
2207-
sync(() => {
2208-
if (self.state._tag === "Exited") {
2209-
return Option.none()
2210-
}
2211-
const fin = Option.fromNullable(self.state.finalizers.get(key))
2212-
self.state.finalizers.delete(key)
2213-
return fin
2214-
}))
2215-
2216-
/* @internal */
2217-
export const releaseMapMake: Effect.Effect<ReleaseMap> = sync((): ReleaseMap => ({
2218-
state: {
2219-
_tag: "Running",
2220-
nextKey: 0,
2221-
finalizers: new Map(),
2222-
update: identity
2223-
}
2224-
}))
2225-
22262089
// -----------------------------------------------------------------------------
22272090
// Cause
22282091
// -----------------------------------------------------------------------------

‎packages/effect/src/internal/fiberRuntime.ts

+103-77
Original file line numberDiff line numberDiff line change
@@ -2668,7 +2668,7 @@ export const scopeWith = <A, E, R>(
26682668

26692669
/* @internal */
26702670
export const scopedEffect = <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, Exclude<R, Scope.Scope>> =>
2671-
core.flatMap(scopeMake(), (scope) => scopeUse(scope)(effect))
2671+
core.flatMap(scopeMake(), (scope) => scopeUse(effect, scope))
26722672

26732673
/* @internal */
26742674
export const sequentialFinalizers = <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
@@ -2988,96 +2988,122 @@ export const withRuntimeFlagsScoped = (
29882988
)
29892989
}
29902990

2991-
// circular with ReleaseMap
2991+
// circular with Scope
2992+
2993+
/** @internal */
2994+
export const scopeTag = Context.GenericTag<Scope.Scope>("effect/Scope")
29922995

29932996
/* @internal */
2994-
export const releaseMapReleaseAll = (
2995-
strategy: ExecutionStrategy.ExecutionStrategy,
2996-
exit: Exit.Exit<unknown, unknown>
2997-
) =>
2998-
(self: core.ReleaseMap): Effect.Effect<void> =>
2999-
core.suspend(() => {
3000-
switch (self.state._tag) {
3001-
case "Exited": {
2997+
export const scope: Effect.Effect<Scope.Scope, never, Scope.Scope> = scopeTag
2998+
2999+
interface ScopeImpl extends Scope.CloseableScope {
3000+
state: {
3001+
readonly _tag: "Open"
3002+
readonly finalizers: Set<Scope.Scope.Finalizer>
3003+
} | {
3004+
readonly _tag: "Closed"
3005+
readonly exit: Exit.Exit<unknown, unknown>
3006+
}
3007+
}
3008+
3009+
const scopeUnsafeAddFinalizer = (scope: ScopeImpl, fin: Scope.Scope.Finalizer): void => {
3010+
if (scope.state._tag === "Open") {
3011+
scope.state.finalizers.add(fin)
3012+
}
3013+
}
3014+
3015+
const ScopeImplProto: Omit<ScopeImpl, "strategy" | "state"> = {
3016+
[core.ScopeTypeId]: core.ScopeTypeId,
3017+
[core.CloseableScopeTypeId]: core.CloseableScopeTypeId,
3018+
pipe() {
3019+
return pipeArguments(this, arguments)
3020+
},
3021+
fork(this: ScopeImpl, strategy) {
3022+
return core.sync(() => {
3023+
const newScope = scopeUnsafeMake(strategy)
3024+
if (this.state._tag === "Closed") {
3025+
newScope.state = this.state
3026+
return newScope
3027+
}
3028+
const fin = (exit: Exit.Exit<unknown, unknown>) => newScope.close(exit)
3029+
this.state.finalizers.add(fin)
3030+
scopeUnsafeAddFinalizer(newScope, (_) =>
3031+
core.sync(() => {
3032+
if (this.state._tag === "Open") {
3033+
this.state.finalizers.delete(fin)
3034+
}
3035+
}))
3036+
return newScope
3037+
})
3038+
},
3039+
close(this: ScopeImpl, exit) {
3040+
return core.suspend(() => {
3041+
if (this.state._tag === "Closed") {
30023042
return core.unit
30033043
}
3004-
case "Running": {
3005-
const finalizersMap = self.state.finalizers
3006-
const update = self.state.update
3007-
const finalizers = Array.from(finalizersMap.keys()).sort((a, b) => b - a).map((key) => finalizersMap.get(key)!)
3008-
self.state = { _tag: "Exited", nextKey: self.state.nextKey, exit, update }
3009-
return executionStrategy.isSequential(strategy) ?
3010-
pipe(
3011-
finalizers,
3012-
core.forEachSequential((fin) => core.exit(update(fin)(exit))),
3013-
core.flatMap((results) =>
3014-
pipe(
3015-
core.exitCollectAll(results),
3016-
Option.map(core.exitAsUnit),
3017-
Option.getOrElse(() => core.exitUnit)
3018-
)
3044+
const finalizers = Array.from(this.state.finalizers.values()).reverse()
3045+
this.state = { _tag: "Closed", exit }
3046+
if (finalizers.length === 0) {
3047+
return core.unit
3048+
}
3049+
return executionStrategy.isSequential(this.strategy) ?
3050+
pipe(
3051+
core.forEachSequential(finalizers, (fin) => core.exit(fin(exit))),
3052+
core.flatMap((results) =>
3053+
pipe(
3054+
core.exitCollectAll(results),
3055+
Option.map(core.exitAsUnit),
3056+
Option.getOrElse(() => core.exitUnit)
30193057
)
3020-
) :
3021-
executionStrategy.isParallel(strategy) ?
3022-
pipe(
3023-
forEachParUnbounded(finalizers, (fin) => core.exit(update(fin)(exit)), false),
3024-
core.flatMap((results) =>
3025-
pipe(
3026-
core.exitCollectAll(results, { parallel: true }),
3027-
Option.map(core.exitAsUnit),
3028-
Option.getOrElse(() => core.exitUnit)
3029-
)
3058+
)
3059+
) :
3060+
executionStrategy.isParallel(this.strategy) ?
3061+
pipe(
3062+
forEachParUnbounded(finalizers, (fin) => core.exit(fin(exit)), false),
3063+
core.flatMap((results) =>
3064+
pipe(
3065+
core.exitCollectAll(results, { parallel: true }),
3066+
Option.map(core.exitAsUnit),
3067+
Option.getOrElse(() => core.exitUnit)
30303068
)
3031-
) :
3032-
pipe(
3033-
forEachParN(finalizers, strategy.parallelism, (fin) => core.exit(update(fin)(exit)), false),
3034-
core.flatMap((results) =>
3035-
pipe(
3036-
core.exitCollectAll(results, { parallel: true }),
3037-
Option.map(core.exitAsUnit),
3038-
Option.getOrElse(() => core.exitUnit)
3039-
)
3069+
)
3070+
) :
3071+
pipe(
3072+
forEachParN(finalizers, this.strategy.parallelism, (fin) => core.exit(fin(exit)), false),
3073+
core.flatMap((results) =>
3074+
pipe(
3075+
core.exitCollectAll(results, { parallel: true }),
3076+
Option.map(core.exitAsUnit),
3077+
Option.getOrElse(() => core.exitUnit)
30403078
)
30413079
)
3080+
)
3081+
})
3082+
},
3083+
addFinalizer(this: ScopeImpl, fin) {
3084+
return core.suspend(() => {
3085+
if (this.state._tag === "Closed") {
3086+
return fin(this.state.exit)
30423087
}
3043-
}
3044-
})
3045-
3046-
// circular with Scope
3047-
3048-
/** @internal */
3049-
export const scopeTag = Context.GenericTag<Scope.Scope>("effect/Scope")
3088+
this.state.finalizers.add(fin)
3089+
return core.unit
3090+
})
3091+
}
3092+
}
30503093

3051-
/* @internal */
3052-
export const scope: Effect.Effect<Scope.Scope, never, Scope.Scope> = scopeTag
3094+
const scopeUnsafeMake = (
3095+
strategy: ExecutionStrategy.ExecutionStrategy = executionStrategy.sequential
3096+
): ScopeImpl => {
3097+
const scope = Object.create(ScopeImplProto)
3098+
scope.strategy = strategy
3099+
scope.state = { _tag: "Open", finalizers: new Set() }
3100+
return scope
3101+
}
30533102

30543103
/* @internal */
30553104
export const scopeMake = (
30563105
strategy: ExecutionStrategy.ExecutionStrategy = executionStrategy.sequential
3057-
): Effect.Effect<Scope.Scope.Closeable> =>
3058-
core.map(core.releaseMapMake, (rm): Scope.Scope.Closeable => ({
3059-
[core.ScopeTypeId]: core.ScopeTypeId,
3060-
[core.CloseableScopeTypeId]: core.CloseableScopeTypeId,
3061-
strategy,
3062-
pipe() {
3063-
return pipeArguments(this, arguments)
3064-
},
3065-
fork: (strategy) =>
3066-
core.uninterruptible(
3067-
pipe(
3068-
scopeMake(strategy),
3069-
core.flatMap((scope) =>
3070-
pipe(
3071-
core.releaseMapAdd(rm, (exit) => core.scopeClose(scope, exit)),
3072-
core.tap((fin) => core.scopeAddFinalizerExit(scope, fin)),
3073-
core.as(scope)
3074-
)
3075-
)
3076-
)
3077-
),
3078-
close: (exit) => core.asUnit(releaseMapReleaseAll(strategy, exit)(rm)),
3079-
addFinalizer: (fin) => core.asUnit(core.releaseMapAdd(fin)(rm))
3080-
}))
3106+
): Effect.Effect<Scope.Scope.Closeable> => core.sync(() => scopeUnsafeMake(strategy))
30813107

30823108
/* @internal */
30833109
export const scopeExtend = dual<

0 commit comments

Comments
 (0)
Please sign in to comment.