63 lines
2.4 KiB
TypeScript
63 lines
2.4 KiB
TypeScript
/**
|
|
* Timeout suspension for in-flight host-side eval bridge calls.
|
|
*
|
|
* The eval watchdog caps a cell's `timeout` as a budget on the cell runtime's
|
|
* own work. Host-side `agent()` / `parallel()` / `completion()` bridge calls hand
|
|
* control to the outer TypeScript process, where the Python kernel or JS VM is
|
|
* only waiting for a result. While that delegated work is in flight, the cell
|
|
* timeout must be ignored completely; once the bridge returns and the runtime is
|
|
* back in control, the watchdog starts a fresh timeout window.
|
|
*
|
|
* Bridge helpers express that handoff with synthetic pause/resume status events
|
|
* on the existing `emitStatus → onStatus` path. Consumers MUST treat these as
|
|
* timeout-control events only: update the watchdog and drop them from rendered
|
|
* or persisted cell output.
|
|
*/
|
|
import type { JsStatusEvent } from "./js/shared/types";
|
|
|
|
/** Synthetic status op emitted when a bridge call leaves the cell runtime. */
|
|
export const EVAL_TIMEOUT_PAUSE_OP = "timeout-pause";
|
|
|
|
/** Synthetic status op emitted when a bridge call returns control to the runtime. */
|
|
export const EVAL_TIMEOUT_RESUME_OP = "timeout-resume";
|
|
|
|
/** Whether a status event is pure eval-timeout control and should not render. */
|
|
export function isEvalTimeoutControlEvent(event: JsStatusEvent): boolean {
|
|
return event.op === EVAL_TIMEOUT_PAUSE_OP || event.op === EVAL_TIMEOUT_RESUME_OP;
|
|
}
|
|
|
|
/** Optional behavior for a timeout pause around a host bridge call. */
|
|
export interface BridgeTimeoutPauseOptions {
|
|
/**
|
|
* Marks the pause as an `agent()` call whose already-started work must finish
|
|
* before an external eval abort reaches the kernel.
|
|
*/
|
|
deferExternalAbort?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Run {@link operation} while suspending the eval watchdog through
|
|
* {@link emitStatus}. A no-op wrapper when no status sink is wired.
|
|
*/
|
|
export async function withBridgeTimeoutPause<T>(
|
|
emitStatus: ((event: JsStatusEvent) => void) | undefined,
|
|
operation: () => Promise<T>,
|
|
options?: BridgeTimeoutPauseOptions,
|
|
): Promise<T> {
|
|
if (!emitStatus) return operation();
|
|
emitStatus(
|
|
options?.deferExternalAbort
|
|
? { op: EVAL_TIMEOUT_PAUSE_OP, deferExternalAbort: true }
|
|
: { op: EVAL_TIMEOUT_PAUSE_OP },
|
|
);
|
|
try {
|
|
return await operation();
|
|
} finally {
|
|
emitStatus(
|
|
options?.deferExternalAbort
|
|
? { op: EVAL_TIMEOUT_RESUME_OP, deferExternalAbort: true }
|
|
: { op: EVAL_TIMEOUT_RESUME_OP },
|
|
);
|
|
}
|
|
}
|