refactor(subagent): unify async readiness and cancellation
This commit is contained in:
49 files changed
+1350
-4147
No files matched your search
@@ -14,11 +14,6 @@
|
||||
* A `cancel` arriving instead of `go` still releases the gate: `drive()`
|
||||
* sees the cancelled state and settles without running the body.
|
||||
*
|
||||
* Terminal ordering is Result first, settlement cleanup second. The session
|
||||
* queues the Result message before asking the execution to reap stray children;
|
||||
* MessagePort FIFO therefore lets the host atomically claim the result before a
|
||||
* cleanup ChildCancel can invoke arbitrary provider code.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-workflow-workerthread/session
|
||||
*/
|
||||
|
||||
@@ -64,10 +59,6 @@ class RpcChildHandle implements ChildHandle {
|
||||
this.result = entry.settled.promise
|
||||
}
|
||||
|
||||
cancel(reason?: string): void {
|
||||
this.post(WorkerToHostType.ChildCancel, { callId: this.callId, reason })
|
||||
}
|
||||
|
||||
dispose(): Promise<void> {
|
||||
this.post(WorkerToHostType.ChildDispose, { callId: this.callId })
|
||||
return this.entry.disposed.promise
|
||||
@@ -76,7 +67,7 @@ class RpcChildHandle implements ChildHandle {
|
||||
|
||||
/**
|
||||
* The worker-side child-RPC bridge ({@link ChildPort}): allocates callIds,
|
||||
* posts the start/cancel/dispose RPCs, and owns the per-call pending
|
||||
* posts the start/dispose RPCs, and owns the per-call pending
|
||||
* book-keeping the session's message handler settles via the `onChild*`
|
||||
* entry points.
|
||||
*/
|
||||
@@ -94,10 +85,10 @@ class ChildRpcBridge implements ChildPort {
|
||||
settled: Promise.withResolvers<ChildResult>(),
|
||||
disposed: Promise.withResolvers<void>(),
|
||||
}
|
||||
// Containment: when synchronous start or asynchronous readiness fails (or
|
||||
// Containment: when asynchronous provider start fails (or
|
||||
// the run is torn down), the settled promise may never gain a consumer —
|
||||
// it must not surface as an unhandled rejection and kill the worker.
|
||||
entry.settled.promise.catch(() => { /* consumed: unconsumed child settlement after failed start/readiness */ })
|
||||
entry.settled.promise.catch(() => { /* consumed: unconsumed child settlement after failed start */ })
|
||||
this.pending.set(callId, entry)
|
||||
this.post(WorkerToHostType.ChildStart, { callId, request })
|
||||
const childId = await entry.started.promise
|
||||
@@ -109,7 +100,7 @@ class ChildRpcBridge implements ChildPort {
|
||||
this.pending.get(callId)?.started.resolve(childId)
|
||||
}
|
||||
|
||||
/** Synchronous start or asynchronous readiness failed; reject and retire the pending RPC. */
|
||||
/** Asynchronous provider start failed; reject and retire the pending RPC. */
|
||||
onChildStartError(callId: number, rendered: string): void {
|
||||
const entry = this.pending.get(callId)
|
||||
this.pending.delete(callId)
|
||||
@@ -213,12 +204,5 @@ export async function runWorkerSession(port: MessagePort, init: WorkerInit): Pro
|
||||
post(WorkerToHostType.Ready, {})
|
||||
await gate.promise
|
||||
const result = await execution.drive()
|
||||
try {
|
||||
// This post is the worker's terminal claim. Queue it BEFORE aborting stray
|
||||
// children: MessagePort FIFO then guarantees the host claims Result before
|
||||
// any settlement-only ChildCancel can invoke arbitrary provider callbacks.
|
||||
post(WorkerToHostType.Result, { result })
|
||||
} finally {
|
||||
execution.reapAfterResult()
|
||||
}
|
||||
post(WorkerToHostType.Result, { result })
|
||||
}
|
||||
Reference in New Issue
Block a user