Skip to content
File

Blob: src/worker/dispatch/shared/run-execution-context/process-tree.ts

typescript156 lines
1import type { ExecutionSession, Process, Sandbox } from "@cloudflare/sandbox";
2 
3import { isNoContainerInstanceError } from "@/worker/sandbox/container-errors";
4 
5import { logger, sleep } from "./shared";
6import type { RunExecutionContextState, RunExecutionScope } from "./types";
7 
8const isLiveProcess = (process: Process): boolean => process.status === "starting" || process.status === "running";
9 
10export const getLiveCurrentProcess = (state: RunExecutionContextState): Process | null =>
11 state.currentProcess && isLiveProcess(state.currentProcess) ? state.currentProcess : null;
12 
13export const isProcessTreeAlive = async (session: ExecutionSession): Promise<boolean> => {
14 try {
15 const processes = await session.listProcesses();
16 return processes.some(isLiveProcess);
17 } catch (error) {
18 if (isNoContainerInstanceError(error)) {
19 return false;
20 }
21 
22 throw error;
23 }
24};
25 
26export const softCancelProcessTree = async (
27 scope: RunExecutionScope,
28 state: RunExecutionContextState,
29 session: ExecutionSession,
30 process: Process | null,
31): Promise<void> => {
32 try {
33 if (process) {
34 await session.killProcess(process.id, "SIGTERM");
35 } else {
36 await session.killAllProcesses();
37 }
38 } catch (error) {
39 logger.warn("run_soft_cancel_failed", {
40 ...scope.logContext,
41 phase: state.phase,
42 error: error instanceof Error ? error.message : String(error),
43 });
44 }
45 
46 try {
47 await session.killAllProcesses();
48 } catch (error) {
49 logger.warn("run_soft_cancel_tree_failed", {
50 ...scope.logContext,
51 phase: state.phase,
52 error: error instanceof Error ? error.message : String(error),
53 });
54 }
55};
56 
57export const hardCancelProcessTree = async (
58 scope: RunExecutionScope,
59 state: RunExecutionContextState,
60 session: ExecutionSession,
61 process: Process | null,
62): Promise<void> => {
63 try {
64 if (process) {
65 await session.killProcess(process.id, "SIGKILL");
66 }
67 } catch (error) {
68 logger.warn("run_hard_cancel_failed", {
69 ...scope.logContext,
70 phase: state.phase,
71 error: error instanceof Error ? error.message : String(error),
72 });
73 }
74 
75 try {
76 await session.killAllProcesses();
77 } catch (error) {
78 logger.warn("run_hard_cancel_tree_failed", {
79 ...scope.logContext,
80 phase: state.phase,
81 error: error instanceof Error ? error.message : String(error),
82 });
83 }
84};
85 
86export const waitForProcessTreeToStop = async (
87 session: ExecutionSession,
88 timeoutMs: number,
89 pollIntervalMs = 250,
90): Promise<boolean> => {
91 const deadline = Date.now() + timeoutMs;
92 
93 while (Date.now() < deadline) {
94 if (!(await isProcessTreeAlive(session))) {
95 try {
96 await session.cleanupCompletedProcesses();
97 } catch {}
98 return true;
99 }
100 
101 await sleep(pollIntervalMs);
102 }
103 
104 return !(await isProcessTreeAlive(session));
105};
106 
107export const waitForProcessTreeToStopSafely = async (
108 scope: RunExecutionScope,
109 session: ExecutionSession,
110 timeoutMs: number,
111 cleanupPhase: string,
112): Promise<boolean> => {
113 try {
114 return await waitForProcessTreeToStop(session, timeoutMs);
115 } catch (error) {
116 logger.warn("run_process_tree_wait_failed", {
117 ...scope.logContext,
118 phase: cleanupPhase,
119 error: error instanceof Error ? error.message : String(error),
120 });
121 return false;
122 }
123};
124 
125export const destroySandbox = async (
126 scope: RunExecutionScope,
127 state: RunExecutionContextState,
128 sandbox: Pick<Sandbox, "destroy" | "setKeepAlive">,
129): Promise<boolean> => {
130 try {
131 await sandbox.setKeepAlive(false);
132 } catch (error) {
133 logger.warn("sandbox_keep_alive_release_failed", {
134 ...scope.logContext,
135 phase: state.phase,
136 error: error instanceof Error ? error.message : String(error),
137 });
138 }
139 
140 try {
141 await sandbox.destroy();
142 return true;
143 } catch (error) {
144 if (isNoContainerInstanceError(error)) {
145 return true;
146 }
147 
148 logger.warn("sandbox_destroy_failed", {
149 ...scope.logContext,
150 phase: state.phase,
151 error: error instanceof Error ? error.message : String(error),
152 });
153 return false;
154 }
155};