fix(process): preserve owning operation cancellation
This commit is contained in:
1 parent
bf3475867f
commit
50dbd875cf
2 files changed
+44
-3
No files matched your search
@@ -70,3 +70,35 @@ it("does not lose an unexpected producer rejection after its final caller cancel
|
||||
reject?.(cause);
|
||||
await expect(waiting).rejects.toBe(cause);
|
||||
});
|
||||
|
||||
it.each(["caller", "owner"])(
|
||||
"preserves %s interruption when owning cancellation and closure occur together",
|
||||
async (first) => {
|
||||
const completed = createDeferred<string>();
|
||||
const controller = new AbortController();
|
||||
const reason = { caller: "operation owner" };
|
||||
const attempt = new SharedStartupAttempt(
|
||||
() => completed.promise,
|
||||
controller.signal,
|
||||
);
|
||||
const waiting = attempt.wait();
|
||||
if (first === "caller") {
|
||||
controller.abort(reason);
|
||||
attempt.close();
|
||||
} else {
|
||||
attempt.close();
|
||||
expect(attempt.signal.aborted).toBe(true);
|
||||
expect(attempt.signal.reason).toBe(STARTUP_OWNER_CLOSED);
|
||||
controller.abort(reason);
|
||||
}
|
||||
expect(attempt.signal.reason).toBe(
|
||||
first === "caller" ? reason : STARTUP_OWNER_CLOSED,
|
||||
);
|
||||
expect(attempt.settled).toBe(false);
|
||||
completed.resolve("finalized");
|
||||
await expect(waiting).resolves.toEqual({
|
||||
status: "completed",
|
||||
value: "finalized",
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -18,11 +18,20 @@ type StartupSettlement<Value> =
|
||||
export class SharedStartupAttempt<Value> {
|
||||
readonly #controller = new AbortController();
|
||||
readonly promise: Promise<Value>;
|
||||
readonly #signal: AbortSignal;
|
||||
#waiters = 0;
|
||||
#settlement: StartupSettlement<Value> = { status: "pending" };
|
||||
|
||||
constructor(start: (signal: AbortSignal) => Promise<Value>) {
|
||||
this.promise = start(this.#controller.signal);
|
||||
constructor(
|
||||
start: (signal: AbortSignal) => Promise<Value>,
|
||||
/** An owning operation's cancellation is independent of individual waiters. */
|
||||
ownerSignal?: AbortSignal,
|
||||
) {
|
||||
this.#signal =
|
||||
ownerSignal === undefined
|
||||
? this.#controller.signal
|
||||
: AbortSignal.any([ownerSignal, this.#controller.signal]);
|
||||
this.promise = start(this.#signal);
|
||||
void this.promise.then(
|
||||
(value) => {
|
||||
this.#settlement = { status: "completed", value };
|
||||
@@ -35,7 +44,7 @@ export class SharedStartupAttempt<Value> {
|
||||
|
||||
/** Signal consumed by the startup producer, never by an individual waiter. */
|
||||
get signal(): AbortSignal {
|
||||
return this.#controller.signal;
|
||||
return this.#signal;
|
||||
}
|
||||
|
||||
/** Completion includes the producer's final cleanup, not just its abort. */
|
||||
|
||||
Reference in new issue
Block a user