Skip to content

Commit 56d18b8

Browse files
committed
Sync between parent and child before starting timer
Avoid setrestrictions() in child interfering wall time. Please note that it might still occur when Wall time >> CPU time on Linux kernel < 6.3, as the lock contention in kernel affects page fault handling.
1 parent 3c6e72d commit 56d18b8

File tree

1 file changed

+33
-2
lines changed

1 file changed

+33
-2
lines changed

judge/runguard.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,7 @@ int main(int argc, char **argv)
983983
size_t data_passed[3];
984984
size_t total_data;
985985
char str[256];
986+
int sync_pipefd[3][2];
986987

987988
struct itimerval itimer;
988989
struct sigaction sigact;
@@ -1167,6 +1168,13 @@ int main(int argc, char **argv)
11671168
if ( pipe(child_pipefd[i])!=0 ) error(errno,"creating pipe for fd %d",i);
11681169
}
11691170

1171+
/* Setup pipes for notifying parent and child before starting timers.
1172+
sync_pipefd[1] is used for parent->child notification (parent closes its PIPE_IN to signal).
1173+
sync_pipefd[2] is used for child->parent notification (child closes its PIPE_IN to signal). */
1174+
for(i=1; i<=2; i++) {
1175+
if ( pipe2(sync_pipefd[i], O_CLOEXEC)!=0 ) error(errno,"creating sync pipe");
1176+
}
1177+
11701178
if ( sigemptyset(&emptymask)!=0 ) error(errno,"creating empty signal mask");
11711179

11721180
/* unmask all signals, except SIGCHLD: detected in pselect() below */
@@ -1261,8 +1269,19 @@ int main(int argc, char **argv)
12611269
error(errno,"closing pipe for fd %d",i);
12621270
}
12631271
}
1272+
if ( close(sync_pipefd[1][PIPE_IN])!=0 || close(sync_pipefd[2][PIPE_OUT])!=0 ) {
1273+
error(errno,"closing pipe for sync_pipefd");
1274+
}
12641275
verbose("pipes closed in child");
12651276

1277+
/* block until parent is ready (sync_pipefd[1][PIPE_OUT] closes).
1278+
If we read() and get 0 bytes, we know that parent has closed sync_pipefd[1][PIPE_IN].
1279+
And it would be unexpected if we get data from the parent before it (show an error). */
1280+
if ( read(sync_pipefd[1][PIPE_OUT], NULL, 0) != 0 ) {
1281+
error(errno, "unexpected sync_pipefd read error in child");
1282+
}
1283+
/* We don't need to explicitly close sync_pipefd[2]: it will atomically be closed on execve(). */
1284+
12661285
/* And execute child command. */
12671286
execvp(cmdname,cmdargs);
12681287
error(errno,"cannot start `%s'",cmdname);
@@ -1277,14 +1296,15 @@ int main(int argc, char **argv)
12771296
verbose("watchdog using user ID `%d'",getuid());
12781297
}
12791298

1280-
if ( gettimeofday(&starttime,NULL) ) error(errno,"getting time");
1281-
12821299
/* Close unused file descriptors */
12831300
for(i=1; i<=2; i++) {
12841301
if ( close(child_pipefd[i][PIPE_IN])!=0 ) {
12851302
error(errno,"closing pipe for fd %i",i);
12861303
}
12871304
}
1305+
if ( close(sync_pipefd[1][PIPE_OUT])!=0 || close(sync_pipefd[2][PIPE_IN])!=0 ) {
1306+
error(errno,"closing pipe for sync_pipefd");
1307+
}
12881308

12891309
/* Redirect child stdout/stderr to file */
12901310
for(i=1; i<=2; i++) {
@@ -1341,6 +1361,17 @@ int main(int argc, char **argv)
13411361
verbose("setting hard wall-time limit to %.3f seconds",walltimelimit[1]);
13421362
}
13431363

1364+
/* Close PIPE_IN of sync_pipefd[1]: allow child to execve() */
1365+
if ( close(sync_pipefd[1][PIPE_IN])!=0 ) {
1366+
error(errno,"closing pipe for sync_pipefd[1] (IN)");
1367+
}
1368+
/* Wait before starting timers. Use read() like what is done in child */
1369+
if ( read(sync_pipefd[2][PIPE_OUT], NULL, 0) != 0 ) {
1370+
error(errno, "unexpected sync_pipefd read error in parent");
1371+
}
1372+
1373+
if ( gettimeofday(&starttime,NULL) ) error(errno,"getting time");
1374+
13441375
if ( times(&startticks)==(clock_t) -1 ) {
13451376
error(errno,"getting start clock ticks");
13461377
}

0 commit comments

Comments
 (0)