Skip to content

Commit a31f8bb

Browse files
Copilotna-trium-144
andcommitted
Add promise tracking to handle worker interruption properly
- Track pending Comlink promises in pendingPromises ref - Add trackPromise helper to wrap worker API calls - Reject all pending promises when worker is terminated/restarted - Prevents promises from hanging forever on worker interruption - Fixes issue where runCommand(), runFiles(), checkSyntax() would never resolve during restart Co-authored-by: na-trium-144 <100704180+na-trium-144@users.noreply.github.com>
1 parent f1953d6 commit a31f8bb

3 files changed

Lines changed: 43 additions & 11 deletions

File tree

app/terminal/worker/runtime.tsx

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ export function WorkerProvider({
5555
const interruptBuffer = useRef<Uint8Array | null>(null);
5656
const capabilities = useRef<WorkerCapabilities | null>(null);
5757
const commandHistory = useRef<string[]>([]);
58+
59+
// Track pending promises for restart-based interruption
60+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
61+
const pendingPromises = useRef<Set<(reason: any) => void>>(new Set());
5862

5963
const initializeWorker = useCallback(async () => {
6064
if (!mutex.isLocked()) {
@@ -96,6 +100,30 @@ export function WorkerProvider({
96100
const [doInit, setDoInit] = useState(false);
97101
const init = useCallback(() => setDoInit(true), []);
98102

103+
// Helper function to wrap worker API calls and track pending promises
104+
// This ensures promises are rejected when the worker is terminated
105+
const trackPromise = useCallback(
106+
<T,>(promise: Promise<T>): Promise<T> => {
107+
return new Promise((resolve, reject) => {
108+
// Store the reject function
109+
pendingPromises.current.add(reject);
110+
111+
promise
112+
.then((result) => {
113+
// Remove reject function on success
114+
pendingPromises.current.delete(reject);
115+
resolve(result);
116+
})
117+
.catch((error) => {
118+
// Remove reject function on error
119+
pendingPromises.current.delete(reject);
120+
reject(error);
121+
});
122+
});
123+
},
124+
[]
125+
);
126+
99127
// Initialization effect
100128
useEffect(() => {
101129
if (doInit) {
@@ -123,6 +151,11 @@ export function WorkerProvider({
123151
}
124152
break;
125153
case "restart": {
154+
// Reject all pending promises
155+
const error = "Worker interrupted";
156+
pendingPromises.current.forEach((reject) => reject(error));
157+
pendingPromises.current.clear();
158+
126159
// Terminate the worker
127160
workerRef.current?.terminate();
128161
workerRef.current = null;
@@ -169,8 +202,8 @@ export function WorkerProvider({
169202
}
170203

171204
try {
172-
const { output, updatedFiles } = await workerApiRef.current.runCode(
173-
code
205+
const { output, updatedFiles } = await trackPromise(
206+
workerApiRef.current.runCode(code)
174207
);
175208

176209
writeFile(updatedFiles);
@@ -191,18 +224,18 @@ export function WorkerProvider({
191224
return [{ type: "error", message: String(error) }];
192225
}
193226
},
194-
[ready, writeFile, mutex]
227+
[ready, writeFile, mutex, trackPromise]
195228
);
196229

197230
const checkSyntax = useCallback(
198231
async (code: string): Promise<SyntaxStatus> => {
199232
if (!workerApiRef.current || !ready) return "invalid";
200233
const { status } = await mutex.runExclusive(() =>
201-
workerApiRef.current!.checkSyntax(code)
234+
trackPromise(workerApiRef.current!.checkSyntax(code))
202235
);
203236
return status;
204237
},
205-
[ready, mutex]
238+
[ready, mutex, trackPromise]
206239
);
207240

208241
const runFiles = useCallback(
@@ -233,15 +266,14 @@ export function WorkerProvider({
233266
interruptBuffer.current[0] = 0;
234267
}
235268
return mutex.runExclusive(async () => {
236-
const { output, updatedFiles } = await workerApiRef.current!.runFile(
237-
filenames[0],
238-
files
269+
const { output, updatedFiles } = await trackPromise(
270+
workerApiRef.current!.runFile(filenames[0], files)
239271
);
240272
writeFile(updatedFiles);
241273
return output;
242274
});
243275
},
244-
[ready, writeFile, mutex]
276+
[ready, writeFile, mutex, trackPromise]
245277
);
246278

247279
return (

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
"remark-gfm": "^4.0.1",
4949
"remark-remove-comments": "^1.1.1",
5050
"swr": "^2.3.6",
51-
"typescript": "^5.9.3",
5251
"zod": "^4.0.17"
5352
},
5453
"devDependencies": {
@@ -71,6 +70,7 @@
7170
"prisma": "^6.18.0",
7271
"tailwindcss": "^4",
7372
"tsx": "^4.20.6",
73+
"typescript": "5.9.3",
7474
"wrangler": "^4.27.0"
7575
}
7676
}

0 commit comments

Comments
 (0)