diff --git a/packages/effect/src/FiberHandle.ts b/packages/effect/src/FiberHandle.ts
index 6ba1aa8d46..4534f0fefe 100644
--- a/packages/effect/src/FiberHandle.ts
+++ b/packages/effect/src/FiberHandle.ts
@@ -276,6 +276,16 @@ export const clear = (self: FiberHandle): Effect.Effect =>
})
)
+const constInterruptedFiber = (function() {
+ let fiber: Fiber.RuntimeFiber | 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.
@@ -308,7 +318,7 @@ export const run: {
if (self.state._tag === "Closed") {
return Effect.interrupt
} else if (self.state.fiber !== undefined && options?.onlyIfMissing === true) {
- return set(self, Effect.runFork(Effect.never), options)
+ return Effect.sync(constInterruptedFiber)
}
return Effect.uninterruptibleMask((restore) =>
Effect.tap(
@@ -324,7 +334,7 @@ export const run: {
if (self.state._tag === "Closed") {
return Effect.interrupt
} else if (self.state.fiber !== undefined && options?.onlyIfMissing === true) {
- return set(self, Effect.runFork(Effect.never), options)
+ return Effect.sync(constInterruptedFiber)
}
return Effect.uninterruptibleMask((restore) =>
Effect.tap(
@@ -390,6 +400,11 @@ export const runtime: (
}
| 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
diff --git a/packages/effect/src/FiberMap.ts b/packages/effect/src/FiberMap.ts
index 16ae906536..9ed52027d2 100644
--- a/packages/effect/src/FiberMap.ts
+++ b/packages/effect/src/FiberMap.ts
@@ -376,6 +376,16 @@ export const clear = (self: FiberMap): Effect.Effect =>
Fiber.interrupt(fiber))
})
+const constInterruptedFiber = (function() {
+ let fiber: Fiber.RuntimeFiber | 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.
@@ -410,8 +420,8 @@ export const run: {
return Effect.suspend(() => {
if (self.state._tag === "Closed") {
return Effect.interrupt
- } else if (unsafeHas(self, key) && options?.onlyIfMissing === true) {
- return set(self, key, Effect.runFork(Effect.never), options)
+ } else if (options?.onlyIfMissing === true && unsafeHas(self, key)) {
+ return Effect.sync(constInterruptedFiber)
}
return Effect.uninterruptibleMask((restore) =>
Effect.tap(
@@ -428,8 +438,8 @@ export const run: {
Effect.suspend(() => {
if (self.state._tag === "Closed") {
return Effect.interrupt
- } else if (unsafeHas(self, key) && options?.onlyIfMissing === true) {
- return set(self, key, Effect.runFork(Effect.never), options)
+ } else if (options?.onlyIfMissing === true && unsafeHas(self, key)) {
+ return Effect.sync(constInterruptedFiber)
}
return Effect.uninterruptibleMask((restore) =>
Effect.tap(
@@ -495,6 +505,11 @@ export const runtime: (
}
| 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
diff --git a/packages/effect/test/FiberHandle.test.ts b/packages/effect/test/FiberHandle.test.ts
index 21b2f0c432..dee7b82472 100644
--- a/packages/effect/test/FiberHandle.test.ts
+++ b/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"
@@ -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", () =>
@@ -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())
+ 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)
+ }))
})
diff --git a/packages/effect/test/FiberMap.test.ts b/packages/effect/test/FiberMap.test.ts
index f89263a47f..246f63b451 100644
--- a/packages/effect/test/FiberMap.test.ts
+++ b/packages/effect/test/FiberMap.test.ts
@@ -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())
+ 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())
+ 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)
+ }))
})