Skip to content

Commit

Permalink
skip running effects in FiberHandle/Map if not required (#2539)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart committed Apr 16, 2024
1 parent cbaf2cd commit 3da0cfa
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-bees-mix.md
@@ -0,0 +1,5 @@
---
"effect": patch
---

skip running effects in FiberHandle/Map if not required
19 changes: 19 additions & 0 deletions packages/effect/src/FiberHandle.ts
Expand Up @@ -276,6 +276,16 @@ export const clear = <A, E>(self: FiberHandle<A, E>): Effect.Effect<void> =>
})
)

const constInterruptedFiber = (function() {
let fiber: Fiber.RuntimeFiber<never, never> | undefined = undefined
return () => {
if (fiber === undefined) {
fiber = Effect.runFork(Effect.interrupt)
}
return fiber
}
})()

/**
* Run an Effect and add the forked fiber to the FiberHandle.
* When the fiber completes, it will be removed from the FiberHandle.
Expand Down Expand Up @@ -307,6 +317,8 @@ export const run: {
return Effect.suspend(() => {
if (self.state._tag === "Closed") {
return Effect.interrupt
} else if (self.state.fiber !== undefined && options?.onlyIfMissing === true) {
return Effect.sync(constInterruptedFiber)
}
return Effect.uninterruptibleMask((restore) =>
Effect.tap(
Expand All @@ -321,6 +333,8 @@ export const run: {
Effect.suspend(() => {
if (self.state._tag === "Closed") {
return Effect.interrupt
} else if (self.state.fiber !== undefined && options?.onlyIfMissing === true) {
return Effect.sync(constInterruptedFiber)
}
return Effect.uninterruptibleMask((restore) =>
Effect.tap(
Expand Down Expand Up @@ -386,6 +400,11 @@ export const runtime: <A, E>(
}
| undefined
) => {
if (self.state._tag === "Closed") {
return constInterruptedFiber()
} else if (self.state.fiber !== undefined && options?.onlyIfMissing === true) {
return constInterruptedFiber()
}
const fiber = runFork(effect, options)
unsafeSet(self, fiber, options)
return fiber
Expand Down
19 changes: 19 additions & 0 deletions packages/effect/src/FiberMap.ts
Expand Up @@ -376,6 +376,16 @@ export const clear = <K, A, E>(self: FiberMap<K, A, E>): Effect.Effect<void> =>
Fiber.interrupt(fiber))
})

const constInterruptedFiber = (function() {
let fiber: Fiber.RuntimeFiber<never, never> | undefined = undefined
return () => {
if (fiber === undefined) {
fiber = Effect.runFork(Effect.interrupt)
}
return fiber
}
})()

/**
* Run an Effect and add the forked fiber to the FiberMap.
* When the fiber completes, it will be removed from the FiberMap.
Expand Down Expand Up @@ -410,6 +420,8 @@ export const run: {
return Effect.suspend(() => {
if (self.state._tag === "Closed") {
return Effect.interrupt
} else if (options?.onlyIfMissing === true && unsafeHas(self, key)) {
return Effect.sync(constInterruptedFiber)
}
return Effect.uninterruptibleMask((restore) =>
Effect.tap(
Expand All @@ -426,6 +438,8 @@ export const run: {
Effect.suspend(() => {
if (self.state._tag === "Closed") {
return Effect.interrupt
} else if (options?.onlyIfMissing === true && unsafeHas(self, key)) {
return Effect.sync(constInterruptedFiber)
}
return Effect.uninterruptibleMask((restore) =>
Effect.tap(
Expand Down Expand Up @@ -491,6 +505,11 @@ export const runtime: <K, A, E>(
}
| undefined
) => {
if (self.state._tag === "Closed") {
return constInterruptedFiber()
} else if (options?.onlyIfMissing === true && unsafeHas(self, key)) {
return constInterruptedFiber()
}
const fiber = runFork(effect, options)
unsafeSet(self, key, fiber, options)
return fiber
Expand Down
30 changes: 27 additions & 3 deletions packages/effect/test/FiberHandle.test.ts
@@ -1,4 +1,4 @@
import { Effect, Ref } from "effect"
import { Effect, Exit, Ref } from "effect"
import * as it from "effect-test/utils/extend"
import * as FiberHandle from "effect/FiberHandle"
import { assert, describe } from "vitest"
Expand Down Expand Up @@ -34,12 +34,12 @@ describe("FiberHandle", () => {
onlyIfMissing: true
})
yield* _(Effect.yieldNow())
assert.strictEqual(yield* _(Ref.get(ref)), 2)
assert.strictEqual(yield* _(Ref.get(ref)), 1)
}),
Effect.scoped
)

assert.strictEqual(yield* _(Ref.get(ref)), 3)
assert.strictEqual(yield* _(Ref.get(ref)), 2)
}))

it.scoped("join", () =>
Expand All @@ -50,4 +50,28 @@ describe("FiberHandle", () => {
const result = yield* _(FiberHandle.join(handle), Effect.flip)
assert.strictEqual(result, "fail")
}))

it.scoped("onlyIfMissing", () =>
Effect.gen(function*(_) {
const handle = yield* _(FiberHandle.make())
const fiberA = yield* _(FiberHandle.run(handle, Effect.never))
const fiberB = yield* _(FiberHandle.run(handle, Effect.never, { onlyIfMissing: true }))
const fiberC = yield* _(FiberHandle.run(handle, Effect.never, { onlyIfMissing: true }))
yield* _(Effect.yieldNow())
assert.isTrue(Exit.isInterrupted(yield* _(fiberB.await)))
assert.isTrue(Exit.isInterrupted(yield* _(fiberC.await)))
assert.strictEqual(fiberA.unsafePoll(), null)
}))

it.scoped("runtime onlyIfMissing", () =>
Effect.gen(function*(_) {
const run = yield* _(FiberHandle.makeRuntime<never>())
const fiberA = run(Effect.never)
const fiberB = run(Effect.never, { onlyIfMissing: true })
const fiberC = run(Effect.never, { onlyIfMissing: true })
yield* _(Effect.yieldNow())
assert.isTrue(Exit.isInterrupted(yield* _(fiberB.await)))
assert.isTrue(Exit.isInterrupted(yield* _(fiberC.await)))
assert.strictEqual(fiberA.unsafePoll(), null)
}))
})
24 changes: 24 additions & 0 deletions packages/effect/test/FiberMap.test.ts
Expand Up @@ -72,4 +72,28 @@ describe("FiberMap", () => {
yield* _(Scope.close(scope, Exit.void))
assert.strictEqual(yield* _(FiberMap.size(set)), 0)
}))

it.scoped("onlyIfMissing", () =>
Effect.gen(function*(_) {
const handle = yield* _(FiberMap.make<string>())
const fiberA = yield* _(FiberMap.run(handle, "a", Effect.never))
const fiberB = yield* _(FiberMap.run(handle, "a", Effect.never, { onlyIfMissing: true }))
const fiberC = yield* _(FiberMap.run(handle, "a", Effect.never, { onlyIfMissing: true }))
yield* _(Effect.yieldNow())
assert.isTrue(Exit.isInterrupted(yield* _(fiberB.await)))
assert.isTrue(Exit.isInterrupted(yield* _(fiberC.await)))
assert.strictEqual(fiberA.unsafePoll(), null)
}))

it.scoped("runtime onlyIfMissing", () =>
Effect.gen(function*(_) {
const run = yield* _(FiberMap.makeRuntime<never, string>())
const fiberA = run("a", Effect.never)
const fiberB = run("a", Effect.never, { onlyIfMissing: true })
const fiberC = run("a", Effect.never, { onlyIfMissing: true })
yield* _(Effect.yieldNow())
assert.isTrue(Exit.isInterrupted(yield* _(fiberB.await)))
assert.isTrue(Exit.isInterrupted(yield* _(fiberC.await)))
assert.strictEqual(fiberA.unsafePoll(), null)
}))
})

0 comments on commit 3da0cfa

Please sign in to comment.