Skip to content

Commit c546918

Browse files
tim-smarteffect-bot
authored andcommitted
add RcMap.invalidate api, for removing a resource from an RcMap (#4278)
1 parent 3b19bcf commit c546918

File tree

6 files changed

+143
-100
lines changed

6 files changed

+143
-100
lines changed

.changeset/new-numbers-visit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"effect": minor
3+
---
4+
5+
add RcMap.invalidate api, for removing a resource from an RcMap

packages/effect/src/Effect.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13845,15 +13845,4 @@ function fnApply(options: {
1384513845
* @since 3.12.0
1384613846
* @category Tracing
1384713847
*/
13848-
export const fnUntraced: fn.Gen = (body: Function, ...pipeables: Array<any>) =>
13849-
pipeables.length === 0
13850-
? function(this: any, ...args: Array<any>) {
13851-
return core.fromIterator(() => body.apply(this, args))
13852-
}
13853-
: function(this: any, ...args: Array<any>) {
13854-
let effect = core.fromIterator(() => body.apply(this, args))
13855-
for (const x of pipeables) {
13856-
effect = x(effect)
13857-
}
13858-
return effect
13859-
}
13848+
export const fnUntraced: fn.Gen = core.fnUntraced

packages/effect/src/RcMap.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,12 @@ export const get: {
109109
* @category combinators
110110
*/
111111
export const keys: <K, A, E>(self: RcMap<K, A, E>) => Effect.Effect<Array<K>, E> = internal.keys
112+
113+
/**
114+
* @since 3.13.0
115+
* @category combinators
116+
*/
117+
export const invalidate: {
118+
<K>(key: K): <A, E>(self: RcMap<K, A, E>) => Effect.Effect<void>
119+
<K, A, E>(self: RcMap<K, A, E>, key: K): Effect.Effect<void>
120+
} = internal.invalidate

packages/effect/src/internal/core.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,6 +1420,20 @@ export const gen: typeof Effect.gen = function() {
14201420
return fromIterator(() => f(pipe))
14211421
}
14221422

1423+
/** @internal */
1424+
export const fnUntraced: Effect.fn.Gen = (body: Function, ...pipeables: Array<any>) =>
1425+
pipeables.length === 0
1426+
? function(this: any, ...args: Array<any>) {
1427+
return fromIterator(() => body.apply(this, args))
1428+
}
1429+
: function(this: any, ...args: Array<any>) {
1430+
let effect = fromIterator(() => body.apply(this, args))
1431+
for (const x of pipeables) {
1432+
effect = x(effect)
1433+
}
1434+
return effect
1435+
}
1436+
14231437
/* @internal */
14241438
export const withConcurrency = dual<
14251439
(concurrency: number | "unbounded") => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>,

packages/effect/src/internal/rcMap.ts

Lines changed: 108 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ declare namespace State {
3232
interface Entry<A, E> {
3333
readonly deferred: Deferred.Deferred<A, E>
3434
readonly scope: Scope.CloseableScope
35+
readonly finalizer: Effect<void>
3536
fiber: RuntimeFiber<void, never> | undefined
3637
refCount: number
3738
}
@@ -121,96 +122,96 @@ export const make: {
121122
export const get: {
122123
<K>(key: K): <A, E>(self: RcMap.RcMap<K, A, E>) => Effect<A, E, Scope.Scope>
123124
<K, A, E>(self: RcMap.RcMap<K, A, E>, key: K): Effect<A, E, Scope.Scope>
124-
} = dual(
125-
2,
126-
<K, A, E>(self_: RcMap.RcMap<K, A, E>, key: K): Effect<A, E, Scope.Scope> => {
127-
const self = self_ as RcMapImpl<K, A, E>
128-
return core.uninterruptibleMask((restore) =>
129-
core.suspend(() => {
130-
if (self.state._tag === "Closed") {
131-
return core.interrupt
132-
}
133-
const state = self.state
134-
const o = MutableHashMap.get(state.map, key)
135-
if (o._tag === "Some") {
136-
const entry = o.value
137-
entry.refCount++
138-
return entry.fiber
139-
? core.as(core.interruptFiber(entry.fiber), entry)
140-
: core.succeed(entry)
141-
} else if (Number.isFinite(self.capacity) && MutableHashMap.size(self.state.map) >= self.capacity) {
142-
return core.fail(
143-
new core.ExceededCapacityException(`RcMap attempted to exceed capacity of ${self.capacity}`)
144-
) as Effect<never>
125+
} = dual(2, <K, A, E>(self_: RcMap.RcMap<K, A, E>, key: K): Effect<A, E, Scope.Scope> => {
126+
const self = self_ as RcMapImpl<K, A, E>
127+
return core.uninterruptibleMask((restore) => getImpl(self, key, restore as any))
128+
})
129+
130+
const getImpl = core.fnUntraced(function*<K, A, E>(self: RcMapImpl<K, A, E>, key: K, restore: <A>(a: A) => A) {
131+
if (self.state._tag === "Closed") {
132+
return yield* core.interrupt
133+
}
134+
const state = self.state
135+
const o = MutableHashMap.get(state.map, key)
136+
let entry: State.Entry<A, E>
137+
if (o._tag === "Some") {
138+
entry = o.value
139+
entry.refCount++
140+
if (entry.fiber) yield* core.interruptFiber(entry.fiber)
141+
} else if (Number.isFinite(self.capacity) && MutableHashMap.size(self.state.map) >= self.capacity) {
142+
return yield* core.fail(
143+
new core.ExceededCapacityException(`RcMap attempted to exceed capacity of ${self.capacity}`)
144+
) as Effect<never>
145+
} else {
146+
entry = yield* self.semaphore.withPermits(1)(acquire(self, key, restore))
147+
}
148+
const scope = yield* fiberRuntime.scopeTag
149+
yield* scope.addFinalizer(() => entry.finalizer)
150+
return yield* restore(core.deferredAwait(entry.deferred))
151+
})
152+
153+
const acquire = core.fnUntraced(function*<K, A, E>(self: RcMapImpl<K, A, E>, key: K, restore: <A>(a: A) => A) {
154+
const scope = yield* fiberRuntime.scopeMake()
155+
const deferred = yield* core.deferredMake<A, E>()
156+
const acquire = self.lookup(key)
157+
yield* restore(core.fiberRefLocally(
158+
acquire as Effect<A, E>,
159+
core.currentContext,
160+
Context.add(self.context, fiberRuntime.scopeTag, scope)
161+
)).pipe(
162+
core.exit,
163+
core.flatMap((exit) => core.deferredDone(deferred, exit)),
164+
circular.forkIn(scope)
165+
)
166+
const entry: State.Entry<A, E> = {
167+
deferred,
168+
scope,
169+
finalizer: undefined as any,
170+
fiber: undefined,
171+
refCount: 1
172+
}
173+
;(entry as any).finalizer = release(self, key, entry)
174+
if (self.state._tag === "Open") {
175+
MutableHashMap.set(self.state.map, key, entry)
176+
}
177+
return entry
178+
})
179+
180+
const release = <K, A, E>(self: RcMapImpl<K, A, E>, key: K, entry: State.Entry<A, E>) =>
181+
core.suspend(() => {
182+
entry.refCount--
183+
if (entry.refCount > 0) {
184+
return core.void
185+
} else if (
186+
self.state._tag === "Closed"
187+
|| !MutableHashMap.has(self.state.map, key)
188+
|| self.idleTimeToLive === undefined
189+
) {
190+
if (self.state._tag === "Open") {
191+
MutableHashMap.remove(self.state.map, key)
192+
}
193+
return core.scopeClose(entry.scope, core.exitVoid)
194+
}
195+
196+
return coreEffect.sleep(self.idleTimeToLive).pipe(
197+
core.interruptible,
198+
core.zipRight(core.suspend(() => {
199+
if (self.state._tag === "Open" && entry.refCount === 0) {
200+
MutableHashMap.remove(self.state.map, key)
201+
return core.scopeClose(entry.scope, core.exitVoid)
145202
}
146-
const acquire = self.lookup(key)
147-
return fiberRuntime.scopeMake().pipe(
148-
coreEffect.bindTo("scope"),
149-
coreEffect.bind("deferred", () => core.deferredMake<A, E>()),
150-
core.tap(({ deferred, scope }) =>
151-
restore(core.fiberRefLocally(
152-
acquire as Effect<A, E>,
153-
core.currentContext,
154-
Context.add(self.context, fiberRuntime.scopeTag, scope)
155-
)).pipe(
156-
core.exit,
157-
core.flatMap((exit) => core.deferredDone(deferred, exit)),
158-
circular.forkIn(scope)
159-
)
160-
),
161-
core.map(({ deferred, scope }) => {
162-
const entry: State.Entry<A, E> = {
163-
deferred,
164-
scope,
165-
fiber: undefined,
166-
refCount: 1
167-
}
168-
MutableHashMap.set(state.map, key, entry)
169-
return entry
170-
})
171-
)
172-
}).pipe(
173-
self.semaphore.withPermits(1),
174-
coreEffect.bindTo("entry"),
175-
coreEffect.bind("scope", () => fiberRuntime.scopeTag),
176-
core.tap(({ entry, scope }) =>
177-
scope.addFinalizer(() =>
178-
core.suspend(() => {
179-
entry.refCount--
180-
if (entry.refCount > 0) {
181-
return core.void
182-
} else if (self.idleTimeToLive === undefined) {
183-
if (self.state._tag === "Open") {
184-
MutableHashMap.remove(self.state.map, key)
185-
}
186-
return core.scopeClose(entry.scope, core.exitVoid)
187-
}
188-
return coreEffect.sleep(self.idleTimeToLive).pipe(
189-
core.interruptible,
190-
core.zipRight(core.suspend(() => {
191-
if (self.state._tag === "Open" && entry.refCount === 0) {
192-
MutableHashMap.remove(self.state.map, key)
193-
return core.scopeClose(entry.scope, core.exitVoid)
194-
}
195-
return core.void
196-
})),
197-
fiberRuntime.ensuring(core.sync(() => {
198-
entry.fiber = undefined
199-
})),
200-
circular.forkIn(self.scope),
201-
core.tap((fiber) => {
202-
entry.fiber = fiber
203-
}),
204-
self.semaphore.withPermits(1)
205-
)
206-
})
207-
)
208-
),
209-
core.flatMap(({ entry }) => restore(core.deferredAwait(entry.deferred)))
210-
)
203+
return core.void
204+
})),
205+
fiberRuntime.ensuring(core.sync(() => {
206+
entry.fiber = undefined
207+
})),
208+
circular.forkIn(self.scope),
209+
core.tap((fiber) => {
210+
entry.fiber = fiber
211+
}),
212+
self.semaphore.withPermits(1)
211213
)
212-
}
213-
)
214+
})
214215

215216
/** @internal */
216217
export const keys = <K, A, E>(self: RcMap.RcMap<K, A, E>): Effect<Array<K>> => {
@@ -219,3 +220,22 @@ export const keys = <K, A, E>(self: RcMap.RcMap<K, A, E>): Effect<Array<K>> => {
219220
impl.state._tag === "Closed" ? core.interrupt : core.succeed(MutableHashMap.keys(impl.state.map))
220221
)
221222
}
223+
224+
/** @internal */
225+
export const invalidate: {
226+
<K>(key: K): <A, E>(self: RcMap.RcMap<K, A, E>) => Effect<void>
227+
<K, A, E>(self: RcMap.RcMap<K, A, E>, key: K): Effect<void>
228+
} = dual(
229+
2,
230+
core.fnUntraced(function*<K, A, E>(self_: RcMap.RcMap<K, A, E>, key: K) {
231+
const self = self_ as RcMapImpl<K, A, E>
232+
if (self.state._tag === "Closed") return
233+
const o = MutableHashMap.get(self.state.map, key)
234+
if (o._tag === "None") return
235+
const entry = o.value
236+
MutableHashMap.remove(self.state.map, key)
237+
if (entry.refCount > 0) return
238+
yield* core.scopeClose(entry.scope, core.exitVoid)
239+
if (entry.fiber) yield* core.interruptFiber(entry.fiber)
240+
})
241+
)

packages/effect/test/RcMap.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ describe("RcMap", () => {
8888

8989
yield* TestClock.adjust(1000)
9090
assert.deepStrictEqual(released, ["foo", "bar"])
91+
92+
yield* Effect.scoped(RcMap.get(map, "baz"))
93+
assert.deepStrictEqual(acquired, ["foo", "bar", "baz"])
94+
yield* RcMap.invalidate(map, "baz")
95+
assert.deepStrictEqual(acquired, ["foo", "bar", "baz"])
96+
assert.deepStrictEqual(released, ["foo", "bar", "baz"])
9197
}))
9298

9399
it.scoped("capacity", () =>

0 commit comments

Comments
 (0)