Skip to content

Commit bac4222

Browse files
committed
Properly distinguish TL from RE (Fixes #41)
1 parent feeba4e commit bac4222

File tree

3 files changed

+46
-46
lines changed

3 files changed

+46
-46
lines changed

src/utils/runtime.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,31 @@ export class Runnable {
1313
private _startTime = 0;
1414
private _endTime = 0;
1515
private _signal: NodeJS.Signals | null = null;
16+
private _timedOut = false;
1617
private _exitCode: number | null = null;
1718

1819
run(command: string, timeout: number, cwd?: string, ...args: string[]) {
19-
this._process = child_process.spawn(command, args, { cwd, timeout });
20+
// FIXME: Simplify TL to check a flag once https://github.com/nodejs/node/pull/51608 lands
21+
22+
const timeoutSignal = AbortSignal.timeout(timeout);
23+
this._process = child_process.spawn(command, args, {
24+
cwd,
25+
signal: timeoutSignal,
26+
});
2027
this._process.stdout.setEncoding('utf-8');
2128
this._process.stderr.setEncoding('utf-8');
2229
this._promise = new Promise((resolve) => {
2330
this._process?.once('spawn', () => {
2431
this._startTime = performance.now();
2532
});
2633
this._process?.once('error', () => {
27-
this._startTime = performance.now();
28-
this._exitCode = -1;
29-
resolve();
34+
this._startTime = performance.now(); // necessary since an invalid command can lead to process not spawned
3035
});
3136
this._process?.once('close', (code, signal) => {
3237
this._endTime = performance.now();
3338
this._signal = signal;
3439
this._exitCode = code;
40+
this._timedOut = timeoutSignal.aborted;
3541
resolve();
3642
});
3743
});
@@ -49,6 +55,9 @@ export class Runnable {
4955
get signal(): NodeJS.Signals | null {
5056
return this._signal;
5157
}
58+
get timedOut(): boolean {
59+
return this._timedOut;
60+
}
5261
get exitCode(): number | null {
5362
return this._exitCode;
5463
}

src/views/judge/provider/JudgeViewProvider.ts

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,20 @@ interface IState
3939
process: Runnable;
4040
}
4141

42-
function getExitCodeStatus(
43-
signal: NodeJS.Signals | null,
44-
code: number | null,
45-
stdout: string,
46-
acceptedStdout: string,
47-
) {
48-
if (signal === 'SIGTERM') return Status.TL;
49-
if (code === null || code) return Status.RE;
50-
if (acceptedStdout === '\n') return Status.NA;
51-
if (stdout === acceptedStdout) return Status.AC;
52-
return Status.WA;
42+
function setTestcaseStats(state: IState, timeLimit: number) {
43+
state.elapsed = state.process.elapsed;
44+
if (state.process.timedOut) {
45+
state.elapsed = timeLimit;
46+
state.status = Status.TL;
47+
} else if (state.process.exitCode === null || state.process.exitCode) {
48+
state.status = Status.RE;
49+
} else if (state.acceptedStdout.data === '\n') {
50+
state.status = Status.NA;
51+
} else if (state.stdout === state.acceptedStdout) {
52+
state.status = Status.AC;
53+
} else {
54+
state.status = Status.WA;
55+
}
5356
}
5457

5558
function coerceToObject(data: unknown): unknown {
@@ -497,19 +500,10 @@ export default class extends BaseViewProvider<ProviderMessage, WebviewMessage> {
497500
});
498501
this._saveFileData();
499502
});
500-
testcase.process.process?.once('close', (exitCode: number | null) => {
503+
testcase.process.process?.once('close', () => {
501504
testcase.process.process?.stderr.removeAllListeners('data');
502505
testcase.process.process?.stdout.removeAllListeners('data');
503-
testcase.status = getExitCodeStatus(
504-
testcase.process.signal,
505-
exitCode,
506-
testcase.stdout.data,
507-
testcase.acceptedStdout.data,
508-
);
509-
testcase.elapsed =
510-
testcase.status === Status.TL
511-
? this._timeLimit
512-
: testcase.process.elapsed;
506+
setTestcaseStats(testcase, this._timeLimit);
513507
super._postMessage({
514508
type: WebviewMessageType.SET,
515509
id,
@@ -703,16 +697,7 @@ export default class extends BaseViewProvider<ProviderMessage, WebviewMessage> {
703697
testcase.acceptedStdout.reset();
704698
testcase.stdin.write(stdin, true);
705699
testcase.acceptedStdout.write(acceptedStdout, true);
706-
testcase.status = getExitCodeStatus(
707-
testcase.process.signal,
708-
testcase.process.exitCode,
709-
testcase.stdout.data,
710-
testcase.acceptedStdout.data,
711-
);
712-
testcase.elapsed =
713-
testcase.status === Status.TL
714-
? this._timeLimit
715-
: testcase.process.elapsed;
700+
setTestcaseStats(testcase, this._timeLimit);
716701

717702
super._postMessage({
718703
type: WebviewMessageType.SET,

src/views/stress/provider/StressViewProvider.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,11 @@ export default class extends BaseViewProvider<ProviderMessage, WebviewMessage> {
223223
cwd,
224224
...generatorRunArguments.slice(1),
225225
);
226-
this._state[0].process.process?.on('error', (data) =>
227-
this._state[0].data.write(data.message, true),
228-
);
226+
this._state[0].process.process?.on('error', (data) => {
227+
if (data.name !== 'AbortError') {
228+
this._state[0].data.write(data.message, true);
229+
}
230+
});
229231
this._state[0].process.process?.stdin.write(`${seed}\n`);
230232
this._state[0].process.process?.stdout.on('data', (data: string) => {
231233
this._state[0].data.write(data, false);
@@ -246,9 +248,11 @@ export default class extends BaseViewProvider<ProviderMessage, WebviewMessage> {
246248
cwd,
247249
...solutionRunArguments.slice(1),
248250
);
249-
this._state[1].process.process?.on('error', (data) =>
250-
this._state[1].data.write(data.message, true),
251-
);
251+
this._state[1].process.process?.on('error', (data) => {
252+
if (data.name !== 'AbortError') {
253+
this._state[1].data.write(data.message, true);
254+
}
255+
});
252256
this._state[1].process.process?.stdout.on('data', (data: string) =>
253257
this._state[1].data.write(data, false),
254258
);
@@ -267,9 +271,11 @@ export default class extends BaseViewProvider<ProviderMessage, WebviewMessage> {
267271
cwd,
268272
...goodSolutionRunArguments.slice(1),
269273
);
270-
this._state[2].process.process?.on('error', (data) =>
271-
this._state[2].data.write(data.message, true),
272-
);
274+
this._state[2].process.process?.on('error', (data) => {
275+
if (data.name !== 'AbortError') {
276+
this._state[2].data.write(data.message, true);
277+
}
278+
});
273279
this._state[2].process.process?.stdout.on('data', (data: string) =>
274280
this._state[2].data.write(data, false),
275281
);
@@ -294,7 +300,7 @@ export default class extends BaseViewProvider<ProviderMessage, WebviewMessage> {
294300
this._state.map((value) => value.process.promise),
295301
);
296302
for (let i = 0; i < 3; i++) {
297-
if (this._state[i].process.signal === 'SIGTERM') {
303+
if (this._state[i].process.timedOut) {
298304
anyFailed = true;
299305
this._state[i].status = Status.TL;
300306
} else if (this._state[i].process.signal === 'SIGUSR1') {

0 commit comments

Comments
 (0)