Skip to content

Commit b604ba6

Browse files
committed
Tweaks for issues noted during testing
1 parent c131fd9 commit b604ba6

15 files changed

Lines changed: 715 additions & 50 deletions

File tree

docs/full-marathon-match-test.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ The tester source can be a standard Topcoder Marathon tester with a `main(...)`
7676

7777
For advanced create payload fields, add `challenge.createPayload` with any valid `POST /v6/challenges` body fields. The script still applies the configured Registration and Submission duration overrides.
7878

79-
This repo includes a `scripts/fixtures/Blocks` example fixture for the TCO22 Blocks Marathon Match. Its manifest includes all sample submissions from the fixture set, including `submission-winner.zip`, plus focused runner regression submissions such as `submission-issue-287-output-limit.zip`, which should fail scoring with an output-size-limit error instead of uploading the oversized public artifact, `submission-issue-245-long-system-callback.zip`, which intentionally keeps the 5,000-test SYSTEM runner alive long enough to validate M2M token refresh before final scoring callbacks, and `submission-issue-21-filesystem-leak.zip`, which validates the submitted-solution filesystem sandbox.
79+
This repo includes a `scripts/fixtures/Blocks` example fixture for the TCO22 Blocks Marathon Match. Its manifest includes all sample submissions from the fixture set, including `submission-winner.zip`, plus focused runner regression submissions such as `submission-issue-287-output-limit.zip`, which should fail scoring with an output-size-limit error instead of uploading the oversized public artifact, `submission-issue-245-long-system-callback.zip`, which intentionally keeps the 5,000-test SYSTEM runner alive long enough to validate M2M token refresh before final scoring callbacks, `submission-issue-21-filesystem-leak.zip`, which validates the submitted-solution filesystem sandbox, and `submission-timeout-under-limit.zip` / `submission-timeout-over-limit.zip`, which bracket a 100 ms measured response window to confirm per-seed timeout behavior.
8080

8181
## Create Fixtures from Production
8282

docs/marathon-match-setup.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ The POST request validates the challenge ID against challenge-api and validates
112112
| `relativeScoringEnabled` | boolean | Enables relative-score normalization |
113113
| `scoreDirection` | string | `MAXIMIZE` or `MINIMIZE` |
114114
| `reviewScorecardId` | string | Review API scorecard used for summations. Must resolve via review-api using either the canonical scorecard id or a legacy scorecard id. |
115-
| `testTimeout` | number | Per-seed tester execution timeout in milliseconds |
115+
| `testTimeout` | number | Per-seed measured submitted-solution execution timeout in milliseconds. Tester setup, initial input writes, artifact IO, and other runner work before the tester starts its timed section are not charged to this limit. |
116116
| `compileTimeout` | number | Submission compile timeout in milliseconds |
117117
| `systemTestTimeout` | number | Total SYSTEM scoring timeout in milliseconds. Defaults to 24 hours (`86400000`) when omitted; if the timeout fires while the ECS task is still active and the SYSTEM summation is incomplete, the API stops the task and writes a failed summation with `metadata.timed_out = true`. |
118118
| `taskDefinitionName` | string | ECS task definition family |

docs/marathon-processor-specification-and-scoring-terminology.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ Compile and test timeouts are configured per Marathon Match challenge.
111111
- `compileTimeout` controls submission compilation timeout.
112112
- For Java submissions, `compileTimeout` also covers class startup and static
113113
initializer checks performed after `javac`.
114-
- `testTimeout` controls per-seed tester execution timeout.
114+
- `testTimeout` controls the per-seed measured submitted-solution execution timeout. Runner setup, initial tester input writes, and artifact IO before the tester starts its timed section are outside this limit; a timed-out seed reports the configured limit as its runtime.
115115
- `systemTestTimeout` controls the total SYSTEM scoring timeout per submission. It defaults to 24 hours and causes the API to stop a still-active ECS runner and write a failed SYSTEM summation with `metadata.timed_out = true`.
116116
- `POST /v6/marathon-match/challenge/:challengeId/rerun/system` restarts existing non-cancelled SYSTEM reviews with the current `testTimeout`, `systemTestTimeout`, SYSTEM seed, and SYSTEM test count settings.
117117

@@ -246,7 +246,7 @@ rustc --edition=2024 -O Solution.rs -o Solution
246246
java -jar Tester.jar -exec "env RUST_BACKTRACE=1 ./Solution" -seed 1
247247
```
248248

249-
Members may print debug information to standard error. The tester captures solution output and error text. Internal artifacts include per-seed `stdout/{seed}.txt` and `stderr/{seed}.txt` files for operator diagnostics, while member-visible EXAMPLE artifacts expose only the approved output summary for the scoring phase, including each seed and the score received for that seed.
249+
Members may print debug information to standard error. The tester captures solution output and error text. Internal artifacts include `compile_log.txt`, `execution-{submissionId}.log`, optional `error-{submissionId}.log`, and per-seed `stdout/{seed}.txt` and `stderr/{seed}.txt` files for operator diagnostics, while member-visible EXAMPLE artifacts expose only the approved output summary for the scoring phase, including each seed and the score received for that seed.
250250

251251
## Download and Access Submissions
252252

docs/submission-phase-scoring.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ Runner task logs are written to CloudWatch using the submission runner log strea
101101

102102
For `PROVISIONAL` and `SYSTEM` scoring, the runner writes progress to the phase review summation metadata through `POST /v6/marathon-match/internal/scoring-progress`. Review API exposes this as `reviewSummation.metadata.testProcess` (`provisional` or `system`), `reviewSummation.metadata.testProgress` (`0` to `1`), and `reviewSummation.metadata.testStatus` (`IN PROGRESS`, `SUCCESS`, or `FAILED`) when metadata is included in the response. In-progress summations keep a neutral placeholder score and must be rendered as unavailable based on `testStatus`; only `FAILED` progress uses the failed-score sentinel. SYSTEM timeout failures also include `reviewSummation.metadata.timed_out = true`.
103103

104-
For generic runner artifacts, only EXAMPLE scoring uploads the member-visible public artifact. Its public `output.txt` includes each testcase ordinal, actual seed, seed score, runtime, runner/tester errors, and submitted-solution stderr, while submitted-solution stdout stays private. Every generic scoring phase uploads private internal artifacts: per-test score details remain in `reviews.json` and callback metadata, while captured submitted-solution output is split into `stdout/{seed}.txt` and `stderr/{seed}.txt` files for each actual seed. PROVISIONAL and SYSTEM scoring do not publish competitor-visible artifacts.
104+
For generic runner artifacts, only EXAMPLE scoring uploads the member-visible public artifact. Its public `output.txt` includes each testcase ordinal, actual seed, seed score, runtime, runner/tester errors, and submitted-solution stderr, while submitted-solution stdout stays private. Every generic scoring phase uploads private internal artifacts: per-test score details remain in `reviews.json` with testcase ordinals and actual seeds, callback metadata keeps member-visible testcase ordinals only, compile and execution logs are preserved as `compile_log.txt`, `execution-{submissionId}.log`, and optional `error-{submissionId}.log`, and captured submitted-solution output is split into `stdout/{seed}.txt` and `stderr/{seed}.txt` files for each actual seed. PROVISIONAL and SYSTEM scoring do not publish competitor-visible artifacts.
105105

106106
When more than one phase config matches the currently open challenge phases, the handler launches one scorer task per match. This is the supported way to run both `EXAMPLE` and `PROVISIONAL` scoring from the same Submission phase.
107107

ecs-runner/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ Once this is saved and the config is active, new scoring launches use the new EC
142142

143143
The runner rejects output once generated public `output.txt` content or a public/private artifact archive would exceed `MM_RUNNER_MAX_OUTPUT_BYTES`. The default is `10000000` bytes. The trusted parent runner passes the resolved limit into the isolated tester child, so generated generic-runner output and uploaded artifact zips use the same cap.
144144

145-
Generic runner member-visible artifacts are uploaded only for EXAMPLE scoring. Their public `output.txt` entries show testcase ordinal, actual seed, seed score, runtime, runner/tester errors, and submitted-solution stderr, while intentionally omitting submitted-solution stdout. Every generic scoring phase uploads private internal artifacts in the `*-internal` zip: `reviews.json` carries per-test `testScores`, and `stdout/{seed}.txt` plus `stderr/{seed}.txt` carry captured submitted-solution output for each actual seed. PROVISIONAL and SYSTEM scoring do not publish competitor-visible artifacts.
145+
Generic runner member-visible artifacts are uploaded only for EXAMPLE scoring. Their public `output.txt` entries show testcase ordinal, actual seed, seed score, runtime, runner/tester errors, and submitted-solution stderr, while intentionally omitting submitted-solution stdout. Every generic scoring phase uploads private internal artifacts in the `*-internal` zip: `reviews.json` carries per-test `metadata.testScores` and top-level `testScores` with testcase ordinals and actual seeds, `compile_log.txt` and `execution-{submissionId}.log` preserve runner diagnostics, `error-{submissionId}.log` is included when present, and `stdout/{seed}.txt` plus `stderr/{seed}.txt` carry captured submitted-solution output for each actual seed. PROVISIONAL and SYSTEM scoring do not publish competitor-visible artifacts.
146146

147147
## Local smoke test
148148

ecs-runner/boilerplate/src/main/java/com/topcoder/marathon/MarathonTester.java

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -101,41 +101,37 @@ public final boolean isTimeout() {
101101
return timeout;
102102
}
103103

104+
/**
105+
* Starts measuring submitted-solution execution time.
106+
*
107+
* <p>Process startup, tester setup, and initial input writes that happen
108+
* before this call are intentionally outside the configured timeout. The
109+
* timeout watcher uses the same monotonic start instant as the elapsed-time
110+
* counter so it cannot consume part of the submitted solution's configured
111+
* runtime before measurement begins.
112+
*/
104113
protected final void startTime() {
105114
synchronized (timeLock) {
106115
if (lastStart != 0) {
107116
System.out.println("ERROR startTime() was called again, before endTime() closed the first one.");
108117
System.exit(-1);
109118
}
119+
lastStart = System.nanoTime();
110120
if (timeLimit > 0) {
121+
final long timeoutDeadline = lastStart + Math.max(1L, timeLimit - elapsedTime);
111122
lastTimeoutThread = new Thread() {
112123
public void run() {
113-
try {
114-
long remainingTime = Math.max(1L, timeLimit - elapsedTime);
115-
boolean finished = process.waitFor(remainingTime, TimeUnit.NANOSECONDS);
116-
if (!finished) {
117-
boolean notifyTimeout = false;
118-
synchronized (timeLock) {
119-
if (lastStart > 0) elapsedTime += System.nanoTime() - lastStart;
120-
lastStart = 0;
121-
if (!timeout) {
122-
timeout = true;
123-
notifyTimeout = true;
124-
}
125-
}
126-
terminateTimedOutProcess();
127-
if (notifyTimeout) timeout();
128-
}
129-
} catch (Exception e) {
130-
}
124+
waitForTimeoutDeadline(timeoutDeadline);
131125
}
132126
};
133127
lastTimeoutThread.start();
134128
}
135-
lastStart = System.nanoTime();
136129
}
137130
}
138131

132+
/**
133+
* Stops measuring submitted-solution execution time.
134+
*/
139135
protected final void stopTime() {
140136
synchronized (timeLock) {
141137
if (lastStart > 0) elapsedTime += System.nanoTime() - lastStart;
@@ -158,12 +154,7 @@ public final double runTest() {
158154
score = getErrorScore();
159155
score = run();
160156
if (timeLimit > 0 && elapsedTime > timeLimit) {
161-
synchronized (timeLock) {
162-
if (!timeout) {
163-
timeout = true;
164-
timeout();
165-
}
166-
}
157+
if (recordTimeout(false)) timeout();
167158
}
168159
end();
169160
} catch (Exception e) {
@@ -190,6 +181,51 @@ public final double runTest() {
190181
return score;
191182
}
192183

184+
/**
185+
* Waits for the submitted process to exit or for the measured timeout
186+
* deadline to pass.
187+
*
188+
* @param timeoutDeadline Monotonic {@link System#nanoTime()} deadline for
189+
* the current measured execution interval.
190+
*/
191+
private void waitForTimeoutDeadline(long timeoutDeadline) {
192+
Process watchedProcess = process;
193+
if (watchedProcess == null) return;
194+
195+
try {
196+
while (true) {
197+
long remainingTime = timeoutDeadline - System.nanoTime();
198+
if (remainingTime <= 0) break;
199+
if (watchedProcess.waitFor(remainingTime, TimeUnit.NANOSECONDS)) return;
200+
}
201+
202+
if (recordTimeout(true)) {
203+
terminateTimedOutProcess();
204+
timeout();
205+
}
206+
} catch (InterruptedException e) {
207+
Thread.currentThread().interrupt();
208+
} catch (Exception e) {
209+
}
210+
}
211+
212+
/**
213+
* Records that the configured measured execution timeout was reached.
214+
*
215+
* @param requireActiveInterval True when timeout should only be recorded if
216+
* {@link #startTime()} is still active.
217+
* @return True when this call transitioned the tester into timeout state.
218+
*/
219+
private boolean recordTimeout(boolean requireActiveInterval) {
220+
synchronized (timeLock) {
221+
if (timeout || (requireActiveInterval && lastStart == 0)) return false;
222+
if (timeLimit > 0) elapsedTime = timeLimit;
223+
lastStart = 0;
224+
timeout = true;
225+
return true;
226+
}
227+
}
228+
193229
/**
194230
* Terminates the submitted solution process after its measured execution
195231
* time expires and closes runner-side streams so blocked tester reads unwind.

0 commit comments

Comments
 (0)