Skip to content

Commit 4d730de

Browse files
committed
autograde_wrapper.py improvements
- spawn a thread that calls wait(). We are pid 1 in the container and need to collect all zombies, otherwise autodriver might get confused - Don't double fork. it's not easy to pass the full exit status of autodriver through a double fork, so use os.execvp instead of subprocess.call. This means we have to do some of the file descriptor munging directly. - Don't bother creating a temporary output file. Directly pipe autodriver's output to the real output file
1 parent 53dfe2e commit 4d730de

File tree

1 file changed

+38
-8
lines changed

1 file changed

+38
-8
lines changed

autodriver/autograde_wrapper.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,61 @@
44
import os
55
import pwd
66
import shutil
7-
import subprocess
7+
import threading
88
#print("Running "+ str(sys.argv))
99

10+
# Wait for all processes, since we are pid 1 in the container,
11+
# terminate once the interesting process exits
12+
class WaitLoop(object):
13+
def __init__(self, pid=None):
14+
self.waitfor=pid
15+
self.status=None
16+
def __call__(self):
17+
try:
18+
(np, self.status)=os.wait()
19+
while pid is None or np != self.waitfor:
20+
(np, self.status)=os.wait()
21+
except OSError:
22+
if pid:
23+
print("Chld process {} never exited, but no more children left".format(self.waitfor))
24+
self.status=-1
25+
1026
for f in os.listdir("mount"):
1127
src=os.path.join("mount", f)
1228
dst=os.path.join("autolab", f)
1329
shutil.copy(src, dst)
1430

1531
autolabuser=pwd.getpwnam("autolab")
32+
(r_p, w_p)=os.pipe()
1633
pid=os.fork()
1734
if pid == 0:
35+
os.close(r_p)
1836
os.setgroups([])
1937
os.setgid(autolabuser.pw_gid)
2038
os.setuid(autolabuser.pw_uid)
21-
outfile=open("output/feedback", "w")
2239
args=["autodriver"]
2340
args.extend(sys.argv[1:])
2441
args.append("autolab")
25-
print("Executing "+str(args), file=outfile)
26-
sys.exit(subprocess.call(args, stdout=outfile, stderr=outfile, close_fds=True))
27-
(np, status)=os.waitpid(pid, 0)
42+
if w_p != 1:
43+
os.dup2(w_p, 1)
44+
if w_p != 2:
45+
os.dup2(w_p, 2)
46+
if w_p > 2:
47+
os.close(w_p)
48+
os.execvp(args[0], args)
49+
os.close(w_p)
50+
waiter=WaitLoop(pid)
51+
thr=threading.Thread(target=waiter)
52+
thr.start()
53+
rpf=os.fdopen(r_p)
54+
shutil.copyfileobj(rpf, open("mount/feedback", "w"))
55+
#print("Copied output")
56+
rpf.close()
57+
thr.join()
2858
# if core, exit -1, else pass through code.
29-
if status & 0xff:
59+
if os.WIFSIGNALED(waiter.status):
3060
status=-1
3161
else:
32-
status>>=8;
33-
shutil.copy("output/feedback", "mount/feedback")
62+
status=os.WEXITSTATUS(waiter.status)
63+
#print("Status is {}".format(status))
3464
sys.exit(status)

0 commit comments

Comments
 (0)