@@ -25,12 +25,41 @@ import {
2525 sessionDir ,
2626 socketPath ,
2727} from '../storage/sessionPaths.js' ;
28+ import { makeAbortError , throwIfAborted } from '../util/abort.js' ;
2829import { invariant } from '../util/assert.js' ;
2930import { sendRpc } from './rpcClient.js' ;
3031
3132const DESTROY_POLL_INTERVAL_MS = 100 ;
3233const DESTROY_MAX_ATTEMPTS = 50 ;
3334
35+ interface PollOptions {
36+ readonly signal ?: AbortSignal ;
37+ }
38+
39+ function pollOptions ( signal ?: AbortSignal ) : PollOptions {
40+ return signal === undefined ? { } : { signal } ;
41+ }
42+
43+ function delayOptions (
44+ signal ?: AbortSignal ,
45+ ) : { signal : AbortSignal } | undefined {
46+ return signal === undefined ? undefined : { signal } ;
47+ }
48+
49+ async function pollDelay (
50+ intervalMs : number ,
51+ signal ?: AbortSignal ,
52+ ) : Promise < void > {
53+ try {
54+ await delay ( intervalMs , undefined , delayOptions ( signal ) ) ;
55+ } catch ( error ) {
56+ if ( signal ?. aborted === true ) {
57+ throw makeAbortError ( signal ) ;
58+ }
59+ throw error ;
60+ }
61+ }
62+
3463interface NodeError extends Error {
3564 code ?: string ;
3665}
@@ -246,6 +275,7 @@ async function waitForTerminalManifest(
246275 manifestFile : string ,
247276 maxAttempts : number = DESTROY_MAX_ATTEMPTS ,
248277 intervalMs : number = DESTROY_POLL_INTERVAL_MS ,
278+ options : PollOptions = { } ,
249279) : Promise < SessionRecord | null > {
250280 invariant (
251281 Number . isInteger ( maxAttempts ) && maxAttempts > 0 ,
@@ -256,15 +286,19 @@ async function waitForTerminalManifest(
256286 'intervalMs must be a non-negative integer' ,
257287 ) ;
258288
289+ const { signal } = options ;
290+ throwIfAborted ( signal ) ;
291+
259292 for ( let attempt = 0 ; attempt < maxAttempts ; attempt += 1 ) {
293+ throwIfAborted ( signal ) ;
260294 const manifest = await readManifest ( manifestFile ) ;
261295
262296 if ( isTerminalSessionStatus ( manifest . status ) ) {
263297 return manifest ;
264298 }
265299
266300 if ( attempt + 1 < maxAttempts ) {
267- await delay ( intervalMs ) ;
301+ await pollDelay ( intervalMs , signal ) ;
268302 }
269303 }
270304
@@ -277,6 +311,7 @@ async function waitForProcessAndSocketShutdown(
277311 socketFile : string ,
278312 maxAttempts : number = DESTROY_MAX_ATTEMPTS ,
279313 intervalMs : number = DESTROY_POLL_INTERVAL_MS ,
314+ options : PollOptions = { } ,
280315) : Promise < boolean > {
281316 invariant (
282317 Number . isInteger ( maxAttempts ) && maxAttempts > 0 ,
@@ -287,7 +322,11 @@ async function waitForProcessAndSocketShutdown(
287322 'intervalMs must be a non-negative integer' ,
288323 ) ;
289324
325+ const { signal } = options ;
326+ throwIfAborted ( signal ) ;
327+
290328 for ( let attempt = 0 ; attempt < maxAttempts ; attempt += 1 ) {
329+ throwIfAborted ( signal ) ;
291330 const hostAlive = isProcessAlive ( hostPid ) ;
292331 const childAlive = isProcessAlive ( childPid ) ;
293332 const socketPresent = await pathExists ( socketFile ) ;
@@ -297,7 +336,7 @@ async function waitForProcessAndSocketShutdown(
297336 }
298337
299338 if ( attempt + 1 < maxAttempts ) {
300- await delay ( intervalMs ) ;
339+ await pollDelay ( intervalMs , signal ) ;
301340 }
302341 }
303342
@@ -458,10 +497,17 @@ export function launchHost(config: LaunchHostConfig): number {
458497 return child . pid ;
459498}
460499
500+ export interface DestroySessionOptions {
501+ readonly signal ?: AbortSignal ;
502+ }
503+
461504export async function destroySession (
462505 sessionId : string ,
463506 force ?: boolean ,
507+ options : DestroySessionOptions = { } ,
464508) : Promise < void > {
509+ const { signal } = options ;
510+ throwIfAborted ( signal ) ;
465511 const { sessionDirectory, manifestFile, socketFile } =
466512 getSessionPaths ( sessionId ) ;
467513 const manifest = await readSessionManifestOrThrow ( sessionId , manifestFile ) ;
@@ -493,6 +539,9 @@ export async function destroySession(
493539 manifest . hostPid ,
494540 manifest . childPid ,
495541 socketFile ,
542+ DESTROY_MAX_ATTEMPTS ,
543+ DESTROY_POLL_INTERVAL_MS ,
544+ pollOptions ( signal ) ,
496545 ) ;
497546 await reconcileSession ( sessionDirectory ) ;
498547
@@ -514,7 +563,8 @@ export async function destroySession(
514563 }
515564
516565 try {
517- await sendRpc ( socketFile , 'destroy' ) ;
566+ throwIfAborted ( signal ) ;
567+ await sendRpc ( socketFile , 'destroy' , undefined , undefined , signal ) ;
518568 } catch ( error ) {
519569 if (
520570 ! ( error instanceof CliError ) ||
@@ -535,7 +585,12 @@ export async function destroySession(
535585 throw error ;
536586 }
537587
538- const terminalManifest = await waitForTerminalManifest ( manifestFile ) ;
588+ const terminalManifest = await waitForTerminalManifest (
589+ manifestFile ,
590+ DESTROY_MAX_ATTEMPTS ,
591+ DESTROY_POLL_INTERVAL_MS ,
592+ pollOptions ( signal ) ,
593+ ) ;
539594 if ( terminalManifest !== null ) {
540595 return ;
541596 }
0 commit comments