diff --git a/packages/core/src/shell.ts b/packages/core/src/shell.ts index c8c23b9502..924ba90cb5 100644 --- a/packages/core/src/shell.ts +++ b/packages/core/src/shell.ts @@ -148,10 +148,10 @@ const layer = () => const removeSession = Effect.fnUntraced(function* (id: Shell.ID) { const session = sessions.get(id) - if (!session) return - sessions.delete(id) const index = exitOrder.indexOf(id) if (index !== -1) exitOrder.splice(index, 1) + if (!session) return + sessions.delete(id) if (session.timeoutFiber) yield* Fiber.interrupt(session.timeoutFiber) // Unblock any wait still pending when the command is removed before it terminated. yield* Deferred.fail(session.done, new NotFoundError({ id })) diff --git a/packages/core/test/tool-shell.test.ts b/packages/core/test/tool-shell.test.ts index 481dd56e37..a2576e9f9c 100644 --- a/packages/core/test/tool-shell.test.ts +++ b/packages/core/test/tool-shell.test.ts @@ -856,6 +856,30 @@ describe("ShellTool", () => { { timeout: 15_000 }, ) + it.live("does not retain removed running shells in exit order", () => + Effect.acquireUseRelease( + Effect.promise(() => tmpdir()), + (tmp) => + withSession(tmp.path, () => + Effect.gen(function* () { + const shell = yield* Shell.Service + yield* Effect.forEach(Array.from({ length: 26 }), () => + Effect.gen(function* () { + const info = yield* shell.create({ command: idleCommand, timeout: 0 }) + yield* shell.remove(info.id) + yield* Effect.sleep(Duration.millis(10)) + }), + ) + + const info = yield* shell.create({ command: helloCommand, timeout: 0 }) + const settled = yield* shell.wait(info.id).pipe(Effect.timeoutOption(Duration.seconds(2))) + expect(settled._tag).toBe("Some") + }), + ), + (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)), + ), + ) + if (!isWindows) { it.live("settles a shell terminated by an external signal", () => Effect.acquireUseRelease(