feat(judge): add timeout handling to runCode and improve error handling
This commit is contained in:
parent
f4077d2f1d
commit
8b744f54f4
@ -73,20 +73,15 @@ async function compileCode(container: Docker.Container, filePath: string, fileNa
|
|||||||
stream.on("end", () => {
|
stream.on("end", () => {
|
||||||
const stdout = stdoutChunks.join("");
|
const stdout = stdoutChunks.join("");
|
||||||
const stderr = stderrChunks.join("");
|
const stderr = stderrChunks.join("");
|
||||||
|
resolve(stderr ? stdout + stderr : stdout);
|
||||||
if (stderr) {
|
|
||||||
resolve(stdout + stderr);
|
|
||||||
} else {
|
|
||||||
resolve(stdout);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.on("error", (error) => reject(error));
|
stream.on("error", reject);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runCode(container: Docker.Container, fileName: string) {
|
async function runCode(container: Docker.Container, fileName: string, timeout: number) {
|
||||||
const runExec = await container.exec({
|
const runExec = await container.exec({
|
||||||
Cmd: [`./${fileName}`],
|
Cmd: [`./${fileName}`],
|
||||||
AttachStdout: true,
|
AttachStdout: true,
|
||||||
@ -94,7 +89,9 @@ async function runCode(container: Docker.Container, fileName: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
runExec.start({}, (error, stream) => {
|
let timeoutHandle: NodeJS.Timeout;
|
||||||
|
|
||||||
|
runExec.start({}, async (error, stream) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
return reject(error);
|
return reject(error);
|
||||||
}
|
}
|
||||||
@ -121,24 +118,38 @@ async function runCode(container: Docker.Container, fileName: string) {
|
|||||||
|
|
||||||
docker.modem.demuxStream(stream, stdoutStream, stderrStream);
|
docker.modem.demuxStream(stream, stdoutStream, stderrStream);
|
||||||
|
|
||||||
|
timeoutHandle = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
console.warn("Execution timed out, stopping container...");
|
||||||
|
await container.stop({ t: 0 });
|
||||||
|
console.warn("Container stopped, removing...");
|
||||||
|
await container.remove();
|
||||||
|
reject(new Error("Execution timed out and container was removed"));
|
||||||
|
} catch (stopError) {
|
||||||
|
reject(new Error("Execution timed out, but failed to stop/remove container"));
|
||||||
|
}
|
||||||
|
}, timeout);
|
||||||
|
|
||||||
stream.on("end", () => {
|
stream.on("end", () => {
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
const stdout = stdoutChunks.join("");
|
const stdout = stdoutChunks.join("");
|
||||||
const stderr = stderrChunks.join("");
|
const stderr = stderrChunks.join("");
|
||||||
if (stderr) {
|
resolve(stderr ? stdout + stderr : stdout);
|
||||||
resolve(stdout + stderr);
|
|
||||||
} else {
|
|
||||||
resolve(stdout);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.on("error", (error) => reject(error));
|
stream.on("error", (error) => {
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cleanupContainer(container: Docker.Container) {
|
async function cleanupContainer(container: Docker.Container) {
|
||||||
try {
|
try {
|
||||||
|
console.log("Stopping container...");
|
||||||
await container.stop({ t: 0 });
|
await container.stop({ t: 0 });
|
||||||
|
console.log("Removing container...");
|
||||||
await container.remove();
|
await container.remove();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Container cleanup failed:", error);
|
console.error("Container cleanup failed:", error);
|
||||||
@ -146,7 +157,7 @@ async function cleanupContainer(container: Docker.Container) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function judge(language: string, value: string) {
|
export async function judge(language: string, value: string) {
|
||||||
const { image, tag, fileName, extension, workingDir } = LanguageConfigs[language];
|
const { image, tag, fileName, extension, workingDir, timeout } = LanguageConfigs[language];
|
||||||
const filePath = `${fileName}.${extension}`;
|
const filePath = `${fileName}.${extension}`;
|
||||||
let container: Docker.Container | undefined;
|
let container: Docker.Container | undefined;
|
||||||
|
|
||||||
@ -162,8 +173,7 @@ export async function judge(language: string, value: string) {
|
|||||||
return compileOutput;
|
return compileOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
const runOutput = await runCode(container, fileName);
|
return await runCode(container, fileName, timeout);
|
||||||
return runOutput;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error during judging:", error);
|
console.error("Error during judging:", error);
|
||||||
throw error;
|
throw error;
|
||||||
|
Loading…
Reference in New Issue
Block a user