@@ -23,6 +23,7 @@ import {
2323 TaskRunExecution ,
2424 timeout ,
2525 TriggerConfig ,
26+ UsageMeasurement ,
2627 waitUntil ,
2728 WorkerManifest ,
2829 WorkerToExecutorMessageCatalog ,
@@ -232,7 +233,10 @@ async function bootstrap() {
232233
233234let _execution : TaskRunExecution | undefined ;
234235let _isRunning = false ;
236+ let _isCancelled = false ;
235237let _tracingSDK : TracingSDK | undefined ;
238+ let _executionMeasurement : UsageMeasurement | undefined ;
239+ const cancelController = new AbortController ( ) ;
236240
237241const zodIpc = new ZodIpcConnection ( {
238242 listenSchema : WorkerToExecutorMessageCatalog ,
@@ -403,18 +407,17 @@ const zodIpc = new ZodIpcConnection({
403407 getNumberEnvVar ( "TRIGGER_RUN_METADATA_FLUSH_INTERVAL" , 1000 )
404408 ) ;
405409
406- const measurement = usage . start ( ) ;
410+ _executionMeasurement = usage . start ( ) ;
407411
408- // This lives outside of the executor because this will eventually be moved to the controller level
409- const signal = execution . run . maxDuration
410- ? timeout . abortAfterTimeout ( execution . run . maxDuration )
411- : undefined ;
412+ const timeoutController = timeout . abortAfterTimeout ( execution . run . maxDuration ) ;
413+
414+ const signal = AbortSignal . any ( [ cancelController . signal , timeoutController . signal ] ) ;
412415
413416 const { result } = await executor . execute ( execution , metadata , traceContext , signal ) ;
414417
415- const usageSample = usage . stop ( measurement ) ;
418+ if ( _isRunning && ! _isCancelled ) {
419+ const usageSample = usage . stop ( _executionMeasurement ) ;
416420
417- if ( _isRunning ) {
418421 return sender . send ( "TASK_RUN_COMPLETED" , {
419422 execution,
420423 result : {
@@ -458,7 +461,16 @@ const zodIpc = new ZodIpcConnection({
458461 WAIT_COMPLETED_NOTIFICATION : async ( ) => {
459462 await managedWorkerRuntime . completeWaitpoints ( [ ] ) ;
460463 } ,
461- FLUSH : async ( { timeoutInMs } , sender ) => {
464+ CANCEL : async ( { timeoutInMs } ) => {
465+ _isCancelled = true ;
466+ cancelController . abort ( "run cancelled" ) ;
467+ await callCancelHooks ( timeoutInMs ) ;
468+ if ( _executionMeasurement ) {
469+ usage . stop ( _executionMeasurement ) ;
470+ }
471+ await flushAll ( timeoutInMs ) ;
472+ } ,
473+ FLUSH : async ( { timeoutInMs } ) => {
462474 await flushAll ( timeoutInMs ) ;
463475 } ,
464476 WAITPOINT_CREATED : async ( { wait, waitpoint } ) => {
@@ -470,6 +482,18 @@ const zodIpc = new ZodIpcConnection({
470482 } ,
471483} ) ;
472484
485+ async function callCancelHooks ( timeoutInMs : number = 10_000 ) {
486+ const now = performance . now ( ) ;
487+
488+ try {
489+ await Promise . race ( [ lifecycleHooks . callOnCancelHookListeners ( ) , setTimeout ( timeoutInMs ) ] ) ;
490+ } finally {
491+ const duration = performance . now ( ) - now ;
492+
493+ log ( `Called cancel hooks in ${ duration } ms` ) ;
494+ }
495+ }
496+
473497async function flushAll ( timeoutInMs : number = 10_000 ) {
474498 const now = performance . now ( ) ;
475499
0 commit comments