comparison unix.py @ 127:f5b8a0c0e3cb

Multiple bug fixes in the unix module Bug fix: a fatal OSError could occur inside Popen.poll() inside unix.call(). Bug fix: CPU-time-wise-ultra-fast testees no longer cause negative zero times to be reported on UNIX. Bug fix (regression in 16fe21d6582e): stdin is no longer read to check for Escape presses when it is not available for reading on UNIX. This would cause Upreckon to block until a key was pressed or, if the testee exceeded the wall-clock time limit, until the testee exit. Bug fix: unix.call() now works on Python 3.2. Bug fix: a fatal RuntimeError no longer occurs on problems with memory limits on systems with RLIMIT_VMEM but no RLIMIT_AS.
author Oleg Oshmyan <chortos@inbox.lv>
date Mon, 16 May 2011 02:53:24 +0100
parents fcdcd0c95552
children 42c8f5c152a5
comparison
equal deleted inserted replaced
126:fcdcd0c95552 127:f5b8a0c0e3cb
49 from select import select, error as SelectError 49 from select import select, error as SelectError
50 from errno import EAGAIN, EINTR 50 from errno import EAGAIN, EINTR
51 from fcntl import fcntl, F_SETFD, F_GETFD, F_SETFL, F_GETFL 51 from fcntl import fcntl, F_SETFD, F_GETFD, F_SETFL, F_GETFL
52 from os import O_NONBLOCK 52 from os import O_NONBLOCK
53 try: 53 try:
54 from signal import siginterrupt
55 except ImportError:
56 # Sucks.
57 siginterrupt = lambda signalnum, flag: None
58 try:
59 import cPickle as pickle 54 import cPickle as pickle
60 except ImportError: 55 except ImportError:
61 import pickle 56 import pickle
62 except ImportError: 57 except ImportError:
63 def call(*args, **kwargs): 58 def call(*args, **kwargs):
91 from fcntl import FD_CLOEXEC 86 from fcntl import FD_CLOEXEC
92 except ImportError: 87 except ImportError:
93 FD_CLOEXEC = 1 88 FD_CLOEXEC = 1
94 89
95 try: 90 try:
91 from signal import siginterrupt
92 except ImportError:
93 # Sucks.
94 siginterrupt = lambda signalnum, flag: None
95
96 try:
96 from resource import getrusage, RUSAGE_SELF, RUSAGE_CHILDREN 97 from resource import getrusage, RUSAGE_SELF, RUSAGE_CHILDREN
97 except ImportError: 98 except ImportError:
98 from time import clock as cpuclock 99 from time import clock as cpuclock
99 getrusage = lambda who: None 100 getrusage = lambda who: None
100 else: 101 else:
105 try: 106 try:
106 from resource import setrlimit 107 from resource import setrlimit
107 try: 108 try:
108 from resource import RLIMIT_AS 109 from resource import RLIMIT_AS
109 except ImportError: 110 except ImportError:
110 from resource import RLIMIT_VMEM 111 from resource import RLIMIT_VMEM as RLIMIT_AS
111 except ImportError: 112 except ImportError:
112 setrlimit = None 113 setrlimit = None
113 114
114 # Make SIGCHLD interrupt sleep() and select() 115 # Make SIGCHLD interrupt sleep() and select()
115 sigchld_pipe_read, sigchld_pipe_write = os.pipe() 116 sigchld_pipe_read, sigchld_pipe_write = os.pipe()
145 setrlimit(RLIMIT_AS, (maxmemory, maxmemory)) 146 setrlimit(RLIMIT_AS, (maxmemory, maxmemory))
146 # I would also set a CPU time limit but I do not want the time 147 # I would also set a CPU time limit but I do not want the time
147 # passing between the calls to fork and exec to be counted in 148 # passing between the calls to fork and exec to be counted in
148 os.write(write, pickle.dumps((clock(), cpuclock()), 1)) 149 os.write(write, pickle.dumps((clock(), cpuclock()), 1))
149 kwargs['preexec_fn'] = preexec_fn 150 kwargs['preexec_fn'] = preexec_fn
151 # So how the hell do I actually make use of pass_fds?
152 # On 3.1-, calling Popen with pass_fds prints an exception
153 # from Popen.__del__ to stderr. On 3.2, Popen without close_fds
154 # or pass_fds creates a child and fails but that of course
155 # generates a SIGCHLD, which causes problems, and I have
156 # no process ID to wait upon to negate the changes made
157 # by the SIGCHLD handler.
158 kwargs['close_fds'] = False
150 old_rusage = getrusage(RUSAGE_CHILDREN) 159 old_rusage = getrusage(RUSAGE_CHILDREN)
151 last_rusage = None 160 last_rusage = None
152 while True: 161 while True:
153 try: 162 try:
154 os.read(sigchld_pipe_read, 512) 163 os.read(sigchld_pipe_read, 512)
181 else: 190 else:
182 if not case.maxwalltime: 191 if not case.maxwalltime:
183 try: 192 try:
184 while case.process.poll() is None: 193 while case.process.poll() is None:
185 s = select((sys.stdin, sigchld_pipe_read), (), ()) 194 s = select((sys.stdin, sigchld_pipe_read), (), ())
186 if (sigchld_pipe_read not in s[0] and 195 if (s[0] == [sys.stdin] and
187 sys.stdin.read(1) == '\33'): 196 sys.stdin.read(1) == '\33'):
188 raise testcases.CanceledByUser 197 raise testcases.CanceledByUser
189 except (SelectError, IOError): 198 except (SelectError, IOError):
190 if sys.exc_info()[1].args[0] != EINTR: 199 if sys.exc_info()[1].args[0] != EINTR:
191 raise 200 raise
197 while case.process.poll() is None: 206 while case.process.poll() is None:
198 remaining = time_end - clock() 207 remaining = time_end - clock()
199 if remaining > 0: 208 if remaining > 0:
200 s = select((sys.stdin, sigchld_pipe_read), 209 s = select((sys.stdin, sigchld_pipe_read),
201 (), (), remaining) 210 (), (), remaining)
202 if (sigchld_pipe_read not in s[0] and 211 if (s[0] == [sys.stdin] and
203 sys.stdin.read(1) == '\33'): 212 sys.stdin.read(1) == '\33'):
204 raise testcases.CanceledByUser 213 raise testcases.CanceledByUser
205 else: 214 else:
206 raise testcases.WallTimeLimitExceeded 215 raise testcases.WallTimeLimitExceeded
207 except (SelectError, IOError): 216 except (SelectError, IOError):
218 case.time_stopped - case.time_started > case.maxwalltime): 227 case.time_stopped - case.time_started > case.maxwalltime):
219 raise testcases.WallTimeLimitExceeded 228 raise testcases.WallTimeLimitExceeded
220 if new_rusage: 229 if new_rusage:
221 time_started = old_rusage.ru_utime + old_rusage.ru_stime + cpustart 230 time_started = old_rusage.ru_utime + old_rusage.ru_stime + cpustart
222 time_stopped = new_rusage.ru_utime + new_rusage.ru_stime 231 time_stopped = new_rusage.ru_utime + new_rusage.ru_stime
232 # Yes, this actually happens
233 if time_started > time_stopped:
234 time_started = time_stopped
223 if case.maxcputime or not case.maxwalltime: 235 if case.maxcputime or not case.maxwalltime:
224 case.time_started = time_started 236 case.time_started = time_started
225 case.time_stopped = time_stopped 237 case.time_stopped = time_stopped
226 case.time_limit_string = case.cpu_time_limit_string 238 case.time_limit_string = case.cpu_time_limit_string
227 if (case.maxcputime and 239 if (case.maxcputime and
245 # To do this, we not only require os.wait4 to be present but also 257 # To do this, we not only require os.wait4 to be present but also
246 # assume things about the implementation of subprocess.Popen. 258 # assume things about the implementation of subprocess.Popen.
247 try: 259 try:
248 def waitpid_emu(pid, options, _wait4=os.wait4): 260 def waitpid_emu(pid, options, _wait4=os.wait4):
249 global last_rusage 261 global last_rusage
250 pid, status, last_rusage = _wait4(pid, options) 262 while True:
263 try:
264 pid, status, last_rusage = _wait4(pid, options)
265 except OSError:
266 if sys.exc_info()[1].errno != EINTR:
267 raise
268 else:
269 break
251 return pid, status 270 return pid, status
252 _waitpid = os.waitpid 271 _waitpid = os.waitpid
253 os.waitpid = waitpid_emu 272 os.waitpid = waitpid_emu
254 try: 273 try:
255 defaults = Popen._internal_poll.__func__.__defaults__ 274 defaults = Popen._internal_poll.__func__.__defaults__