fix(worker): make user-box lifecycle concurrency-safe
This commit is contained in:
@@ -4,9 +4,10 @@ import type { Server } from 'node:http';
|
||||
import type { WebSocket } from 'ws';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
import type { BoxHandle } from './user-container';
|
||||
import { env } from './env';
|
||||
import { verifyTerminalToken } from './terminal-token';
|
||||
import { acquireUserBox, releaseUserBox } from './user-container';
|
||||
import { acquireUserBox } from './user-container';
|
||||
import { getTerminalWorkspace } from './worker';
|
||||
|
||||
const clampDimension = (value: unknown) => {
|
||||
@@ -63,7 +64,7 @@ const bridge = async (ws: WebSocket, jobId: string) => {
|
||||
else pendingInput.push(data);
|
||||
});
|
||||
|
||||
let acquired = false;
|
||||
const handleHolder: { current?: BoxHandle } = {};
|
||||
let released = false;
|
||||
// Read through a function so TS doesn't narrow `released` to a constant — the
|
||||
// cleanup handler flips it asynchronously when the socket closes.
|
||||
@@ -72,7 +73,7 @@ const bridge = async (ws: WebSocket, jobId: string) => {
|
||||
if (released) return;
|
||||
released = true;
|
||||
procHolder.current?.kill();
|
||||
if (acquired) releaseUserBox(workspace.username);
|
||||
handleHolder.current?.release();
|
||||
};
|
||||
ws.on('close', cleanup);
|
||||
ws.on('error', cleanup);
|
||||
@@ -81,12 +82,13 @@ const bridge = async (ws: WebSocket, jobId: string) => {
|
||||
// the terminal share the exact same container (Phase 2).
|
||||
let boxName: string;
|
||||
try {
|
||||
boxName = await acquireUserBox({
|
||||
const handle = await acquireUserBox({
|
||||
username: workspace.username,
|
||||
workdir: workspace.workdir,
|
||||
containerHome: workspace.containerHome,
|
||||
});
|
||||
acquired = true;
|
||||
handleHolder.current = handle;
|
||||
boxName = handle.boxName;
|
||||
} catch (error) {
|
||||
ws.close(
|
||||
1011,
|
||||
@@ -95,7 +97,11 @@ const bridge = async (ws: WebSocket, jobId: string) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReleased()) return; // client disconnected during startup; cleanup ran
|
||||
// Cleanup may have run before the awaited handle existed.
|
||||
if (isReleased()) {
|
||||
handleHolder.current.release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reattach a persistent tmux session across reconnects when available, else a
|
||||
// plain login shell. `stty` sizes the PTY to the client's viewport up front.
|
||||
|
||||
Reference in New Issue
Block a user