comparison testcases.py @ 82:06356af50bf9

Finished testcases reorganization and CPU time limit implementation We now have: * Win32-specific code in the win32 module (including bug fixes), * UNIX-specific and generic code in the unix module, * a much cleaner testcases module, * wait4-based resource limits working on Python 3 (this is a bug fix), * no warning/error reported on non-Win32 when -x is not passed but standard input does not come from a terminal, * the maxtime configuration variable replaced with two new variables named maxcputime and maxwalltime, * CPU time reported if it can be determined unless an error occurs sooner than it is determined (e. g. if the wall-clock time limit is exceeded), * memory limits enforced even if Upreckon's forking already breaks them, * CPU time limits and private virtual memory limits honoured on Win32, * CPU time limits honoured on UNIX(-like) platforms supporting wait4 or getrusage, * address space limits honoured on UNIX(-like) platforms supporting setrlimit with RLIMIT_AS/RLIMIT_VMEM, * resident set size limits honoured on UNIX(-like) platforms supporting wait4.
author Oleg Oshmyan <chortos@inbox.lv>
date Wed, 23 Feb 2011 23:35:27 +0000
parents 24752db487c5
children 37c4ad87583c
comparison
equal deleted inserted replaced
81:24752db487c5 82:06356af50bf9
19 19
20 import os 20 import os
21 devnull = open(os.path.devnull, 'w+') 21 devnull = open(os.path.devnull, 'w+')
22 22
23 try: 23 try:
24 from signal import SIGTERM, SIGKILL 24 from win32 import *
25 except ImportError:
26 SIGTERM = 15
27 SIGKILL = 9
28
29 try:
30 from _subprocess import TerminateProcess
31 except ImportError:
32 # CPython 2.5 does define _subprocess.TerminateProcess even though it is
33 # not used in the subprocess module, but maybe something else does not
34 try:
35 import ctypes
36 TerminateProcess = ctypes.windll.kernel32.TerminateProcess
37 except (ImportError, AttributeError):
38 TerminateProcess = None
39
40
41 # Do not show error messages due to errors in the program being tested
42 try:
43 import ctypes
44 try:
45 errmode = ctypes.windll.kernel32.GetErrorMode()
46 except AttributeError:
47 errmode = ctypes.windll.kernel32.SetErrorMode(0)
48 errmode |= 0x8003
49 ctypes.windll.kernel32.SetErrorMode(errmode)
50 except Exception: 25 except Exception:
51 pass 26 from unix import *
52
53
54 # Do the hacky-wacky dark magic needed to catch presses of the Escape button.
55 # If only Python supported forcible termination of threads...
56 if not sys.stdin.isatty():
57 canceled = init_canceled = lambda: False
58 pause = None
59 else:
60 try:
61 # Windows has select() too, but it is not the select() we want
62 import msvcrt
63 except ImportError:
64 try:
65 from select import select
66 import termios, tty, atexit
67 except ImportError:
68 # It cannot be helped!
69 # Silently disable support for killing the program being tested
70 canceled = init_canceled = lambda: False
71 pause = None
72 else:
73 def cleanup(old=termios.tcgetattr(sys.stdin.fileno())):
74 termios.tcsetattr(sys.stdin.fileno(), termios.TCSAFLUSH, old)
75 atexit.register(cleanup)
76 del cleanup
77 tty.setcbreak(sys.stdin.fileno())
78 def canceled(select=select, stdin=sys.stdin, read=sys.stdin.read):
79 while select((stdin,), (), (), 0)[0]:
80 if read(1) == '\33':
81 return True
82 return False
83 def init_canceled():
84 while select((sys.stdin,), (), (), 0)[0]:
85 sys.stdin.read(1)
86 def pause():
87 sys.stdin.read(1)
88 else:
89 def canceled(kbhit=msvcrt.kbhit, getch=msvcrt.getch):
90 while kbhit():
91 c = getch()
92 if c == '\33':
93 return True
94 elif c == '\0':
95 # Let's hope no-one is fiddling with this
96 getch()
97 return False
98 def init_canceled():
99 while msvcrt.kbhit():
100 msvcrt.getch()
101 def pause():
102 msvcrt.getch()
103
104 try:
105 from signal import SIGCHLD, signal, SIG_DFL
106 from select import select, error as select_error
107 from errno import EINTR
108 import fcntl
109 try:
110 import cPickle as pickle
111 except ImportError:
112 import pickle
113 except ImportError:
114 try:
115 from _subprocess import WAIT_OBJECT_0, STD_INPUT_HANDLE, INFINITE
116 except ImportError:
117 WAIT_OBJECT_0 = 0
118 STD_INPUT_HANDLE = -10
119 INFINITE = -1
120 try:
121 import ctypes
122 SetConsoleMode = ctypes.windll.kernel32.SetConsoleMode
123 FlushConsoleInputBuffer = ctypes.windll.kernel32.FlushConsoleInputBuffer
124 WaitForMultipleObjects = ctypes.windll.kernel32.WaitForMultipleObjects
125 ReadConsoleInputA = ctypes.windll.kernel32.ReadConsoleInputA
126 try:
127 from _subprocess import GetStdHandle
128 except ImportError:
129 GetStdHandle = ctypes.windll.kernel32.GetStdHandle
130 except (ImportError, AttributeError):
131 console_input = False
132 else:
133 hStdin = GetStdHandle(STD_INPUT_HANDLE)
134 console_input = bool(SetConsoleMode(hStdin, 1))
135 if console_input:
136 FlushConsoleInputBuffer(hStdin)
137 class KEY_EVENT_RECORD(ctypes.Structure):
138 _fields_ = (("bKeyDown", ctypes.c_int),
139 ("wRepeatCount", ctypes.c_ushort),
140 ("wVirtualKeyCode", ctypes.c_ushort),
141 ("wVirtualScanCode", ctypes.c_ushort),
142 ("UnicodeChar", ctypes.c_wchar),
143 ("dwControlKeyState", ctypes.c_uint))
144 class INPUT_RECORD(ctypes.Structure):
145 _fields_ = (("EventType", ctypes.c_int),
146 ("KeyEvent", KEY_EVENT_RECORD))
147 # Memory limits (currently) are not supported
148 def call(*args, **kwargs):
149 case = kwargs.pop('case')
150 try:
151 case.process = Popen(*args, **kwargs)
152 except OSError:
153 raise CannotStartTestee(sys.exc_info()[1])
154 case.time_started = clock()
155 if not console_input:
156 if case.maxtime:
157 if WaitForSingleObject(case.process._handle, int(case.maxtime * 1000)) != WAIT_OBJECT_0:
158 raise TimeLimitExceeded
159 else:
160 case.process.wait()
161 else:
162 ir = INPUT_RECORD()
163 n = ctypes.c_int()
164 lpHandles = (ctypes.c_int * 2)(hStdin, case.process._handle)
165 if case.maxtime:
166 time_end = clock() + case.maxtime
167 while case.process.poll() is None:
168 remaining = time_end - clock()
169 if remaining > 0:
170 if WaitForMultipleObjects(2, lpHandles, False, int(remaining * 1000)) == WAIT_OBJECT_0:
171 ReadConsoleInputA(hStdin, ctypes.byref(ir), 1, ctypes.byref(n))
172 if ir.EventType == 1 and ir.KeyEvent.bKeyDown and ir.KeyEvent.wVirtualKeyCode == 27:
173 raise CanceledByUser
174 else:
175 raise TimeLimitExceeded
176 else:
177 while case.process.poll() is None:
178 if WaitForMultipleObjects(2, lpHandles, False, INFINITE) == WAIT_OBJECT_0:
179 ReadConsoleInputA(hStdin, ctypes.byref(ir), 1, ctypes.byref(n))
180 if ir.EventType == 1 and ir.KeyEvent.bKeyDown and ir.KeyEvent.wVirtualKeyCode == 27:
181 raise CanceledByUser
182 case.time_stopped = clock()
183 if not console_input:
184 try:
185 try:
186 from _subprocess import WaitForSingleObject
187 except ImportError:
188 import ctypes
189 WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject
190 except (ImportError, AttributeError):
191 # TODO: move the default implementation here
192 call = None
193 else:
194 # Make SIGCHLD interrupt sleep() and select()
195 def bury_child(signum, frame):
196 try:
197 bury_child.case.time_stopped = clock()
198 except Exception:
199 pass
200 signal(SIGCHLD, bury_child)
201
202 # If you want this to work, don't set any stdio argument to PIPE
203 def call_real(*args, **kwargs):
204 bury_child.case = case = kwargs.pop('case')
205 preexec_fn_ = kwargs.get('preexec_fn', None)
206 read, write = os.pipe()
207 def preexec_fn():
208 os.close(read)
209 if preexec_fn_:
210 preexec_fn_()
211 fcntl.fcntl(write, fcntl.F_SETFD, fcntl.fcntl(write, fcntl.F_GETFD) | getattr(fcntl, 'FD_CLOEXEC', 1))
212 fwrite = os.fdopen(write, 'wb')
213 pickle.dump(clock(), fwrite, 1)
214 kwargs['preexec_fn'] = preexec_fn
215 try:
216 case.process = Popen(*args, **kwargs)
217 except OSError:
218 os.close(read)
219 raise CannotStartTestee(sys.exc_info()[1])
220 finally:
221 os.close(write)
222 try:
223 if pause is None:
224 if case.maxtime:
225 time.sleep(case.maxtime)
226 if case.process.poll() is None:
227 raise TimeLimitExceeded
228 else:
229 case.process.wait()
230 else:
231 if not case.maxtime:
232 try:
233 while case.process.poll() is None:
234 if select((sys.stdin,), (), ())[0]:
235 if sys.stdin.read(1) == '\33':
236 raise CanceledByUser
237 except select_error:
238 if sys.exc_info()[1].args[0] != EINTR:
239 raise
240 else:
241 case.process.poll()
242 else:
243 time_end = clock() + case.maxtime
244 try:
245 while case.process.poll() is None:
246 remaining = time_end - clock()
247 if remaining > 0:
248 if select((sys.stdin,), (), (), remaining)[0]:
249 if sys.stdin.read(1) == '\33':
250 raise CanceledByUser
251 else:
252 raise TimeLimitExceeded
253 except select_error:
254 if sys.exc_info()[1].args[0] != EINTR:
255 raise
256 else:
257 case.process.poll()
258 finally:
259 case.time_started = pickle.loads(os.read(read, 512))
260 os.close(read)
261 del bury_child.case
262 def call(*args, **kwargs):
263 if 'preexec_fn' in kwargs:
264 try:
265 return call_real(*args, **kwargs)
266 except MemoryError:
267 # If there is not enough memory for the forked test.py,
268 # opt for silent dropping of the limit
269 # TODO: show a warning somewhere
270 del kwargs['preexec_fn']
271 return call_real(*args, **kwargs)
272 else:
273 return call_real(*args, **kwargs)
274
275 # Emulate memory limits on platforms compatible with 4.3BSD but not XSI
276 # I say 'emulate' because the OS will allow excessive memory usage
277 # anyway; Upreckon will just treat the test case as not passed.
278 # To do this, we not only require os.wait4 to be present but also
279 # assume things about the implementation of subprocess.Popen.
280 try:
281 def waitpid_emu(pid, options, _wait4=os.wait4):
282 global last_rusage
283 pid, status, last_rusage = _wait4(pid, options)
284 return pid, status
285 _waitpid = os.waitpid
286 os.waitpid = waitpid_emu
287 try:
288 defaults = Popen._internal_poll.__func__.__defaults__
289 except AttributeError:
290 # Python 2.5
291 defaults = Popen._internal_poll.im_func.func_defaults
292 i = defaults.index(_waitpid)
293 defaults = defaults[:i] + (waitpid_emu,) + defaults[i+1:]
294 try:
295 Popen._internal_poll.__func__.__defaults__ = defaults
296 except AttributeError:
297 pass
298 Popen._internal_poll.im_func.func_defaults = defaults
299 except (AttributeError, ValueError):
300 pass
301 27
302 __all__ = ('TestCase', 'load_problem', 'TestCaseNotPassed', 28 __all__ = ('TestCase', 'load_problem', 'TestCaseNotPassed',
303 'TimeLimitExceeded', 'CanceledByUser', 'WrongAnswer', 29 'TimeLimitExceeded', 'CanceledByUser', 'WrongAnswer',
304 'NonZeroExitCode', 'CannotStartTestee', 30 'NonZeroExitCode', 'CannotStartTestee',
305 'CannotStartValidator', 'CannotReadOutputFile', 31 'CannotStartValidator', 'CannotReadOutputFile',
306 'CannotReadInputFile', 'CannotReadAnswerFile', 32 'CannotReadInputFile', 'CannotReadAnswerFile',
307 'MemoryLimitExceeded') 33 'MemoryLimitExceeded', 'CPUTimeLimitExceeded',
34 'WallTimeLimitExceeded')
308 35
309 36
310 37
311 # Exceptions 38 # Exceptions
312 39
313 class TestCaseNotPassed(Exception): __slots__ = () 40 class TestCaseNotPassed(Exception): __slots__ = ()
314 class TimeLimitExceeded(TestCaseNotPassed): __slots__ = () 41 class TimeLimitExceeded(TestCaseNotPassed): __slots__ = ()
42 class CPUTimeLimitExceeded(TimeLimitExceeded): __slots__ = ()
43 class WallTimeLimitExceeded(TimeLimitExceeded): __slots__ = ()
315 class MemoryLimitExceeded(TestCaseNotPassed): __slots__ = () 44 class MemoryLimitExceeded(TestCaseNotPassed): __slots__ = ()
316 class CanceledByUser(TestCaseNotPassed): __slots__ = () 45 class CanceledByUser(TestCaseNotPassed): __slots__ = ()
317 46
318 class WrongAnswer(TestCaseNotPassed): 47 class WrongAnswer(TestCaseNotPassed):
319 __slots__ = 'comment' 48 __slots__ = 'comment'
382 111
383 # Test case types 112 # Test case types
384 113
385 class TestCase(object): 114 class TestCase(object):
386 __slots__ = ('problem', 'id', 'isdummy', 'infile', 'outfile', 'points', 115 __slots__ = ('problem', 'id', 'isdummy', 'infile', 'outfile', 'points',
387 'process', 'time_started', 'time_stopped', 'time_limit_string', 116 'process', 'time_started', 'time_stopped',
388 'realinname', 'realoutname', 'maxtime', 'maxmemory', 117 'realinname', 'realoutname', 'maxcputime', 'maxwalltime',
389 'has_called_back', 'files_to_delete') 118 'maxmemory', 'has_called_back', 'files_to_delete',
119 'cpu_time_limit_string', 'wall_time_limit_string',
120 'time_limit_string')
390 121
391 if ABCMeta: 122 if ABCMeta:
392 __metaclass__ = ABCMeta 123 __metaclass__ = ABCMeta
393 124
394 def __init__(case, prob, id, isdummy, points): 125 def __init__(case, prob, id, isdummy, points):
395 case.problem = prob 126 case.problem = prob
396 case.id = id 127 case.id = id
397 case.isdummy = isdummy 128 case.isdummy = isdummy
398 case.points = points 129 case.points = points
399 case.maxtime = case.problem.config.maxtime 130 case.maxcputime = case.problem.config.maxcputime
131 case.maxwalltime = case.problem.config.maxwalltime
400 case.maxmemory = case.problem.config.maxmemory 132 case.maxmemory = case.problem.config.maxmemory
401 if case.maxtime: 133 if case.maxcputime:
402 case.time_limit_string = '/%.3f' % case.maxtime 134 case.cpu_time_limit_string = '/%.3f' % case.maxcputime
403 else: 135 else:
404 case.time_limit_string = '' 136 case.cpu_time_limit_string = ''
137 if case.maxwalltime:
138 case.wall_time_limit_string = '/%.3f' % case.maxwalltime
139 else:
140 case.wall_time_limit_string = ''
405 if not isdummy: 141 if not isdummy:
406 case.realinname = case.problem.config.testcaseinname 142 case.realinname = case.problem.config.testcaseinname
407 case.realoutname = case.problem.config.testcaseoutname 143 case.realoutname = case.problem.config.testcaseoutname
408 else: 144 else:
409 case.realinname = case.problem.config.dummyinname 145 case.realinname = case.problem.config.dummyinname
413 def test(case): raise NotImplementedError 149 def test(case): raise NotImplementedError
414 150
415 def __call__(case, callback): 151 def __call__(case, callback):
416 case.has_called_back = False 152 case.has_called_back = False
417 case.files_to_delete = [] 153 case.files_to_delete = []
154 case.time_limit_string = case.wall_time_limit_string
418 try: 155 try:
419 return case.test(callback) 156 return case.test(callback)
420 finally: 157 finally:
421 now = clock() 158 now = clock()
422 if not getattr(case, 'time_started', None): 159 if getattr(case, 'time_started', None) is None:
423 case.time_started = case.time_stopped = now 160 case.time_started = case.time_stopped = now
424 elif not getattr(case, 'time_stopped', None): 161 elif getattr(case, 'time_stopped', None) is None:
425 case.time_stopped = now 162 case.time_stopped = now
426 if not case.has_called_back: 163 if not case.has_called_back:
427 callback() 164 callback()
428 case.cleanup() 165 case.cleanup()
429 166
430 def cleanup(case): 167 def cleanup(case):
431 #if getattr(case, 'infile', None): 168 #if getattr(case, 'infile', None):
432 # case.infile.close() 169 # case.infile.close()
433 #if getattr(case, 'outfile', None): 170 #if getattr(case, 'outfile', None):
434 # case.outfile.close() 171 # case.outfile.close()
435 if getattr(case, 'process', None): 172 if getattr(case, 'process', None) and case.process.returncode is None:
436 # Try killing after three unsuccessful TERM attempts in a row 173 # Try KILLing after three unsuccessful TERM attempts in a row
437 # (except on Windows, where TERMing is killing)
438 for i in range(3): 174 for i in range(3):
439 try: 175 try:
440 try: 176 terminate(case.process)
441 case.process.terminate()
442 except AttributeError:
443 # Python 2.5
444 if TerminateProcess and hasattr(proc, '_handle'):
445 # Windows API
446 TerminateProcess(proc._handle, 1)
447 else:
448 # POSIX
449 os.kill(proc.pid, SIGTERM)
450 except Exception: 177 except Exception:
451 time.sleep(0) 178 time.sleep(0)
452 case.process.poll() 179 case.process.poll()
453 else: 180 else:
454 case.process.wait() 181 case.process.wait()
456 else: 183 else:
457 # If killing the process is unsuccessful three times in a row, 184 # If killing the process is unsuccessful three times in a row,
458 # just silently stop trying 185 # just silently stop trying
459 for i in range(3): 186 for i in range(3):
460 try: 187 try:
461 try: 188 kill(case.process)
462 case.process.kill()
463 except AttributeError:
464 # Python 2.5
465 if TerminateProcess and hasattr(proc, '_handle'):
466 # Windows API
467 TerminateProcess(proc._handle, 1)
468 else:
469 # POSIX
470 os.kill(proc.pid, SIGKILL)
471 except Exception: 189 except Exception:
472 time.sleep(0) 190 time.sleep(0)
473 case.process.poll() 191 case.process.poll()
474 else: 192 else:
475 case.process.wait() 193 case.process.wait()
545 263
546 class BatchTestCase(ValidatedTestCase): 264 class BatchTestCase(ValidatedTestCase):
547 __slots__ = () 265 __slots__ = ()
548 266
549 def test(case, callback): 267 def test(case, callback):
550 init_canceled()
551 if sys.platform == 'win32' or not case.maxmemory:
552 preexec_fn = None
553 else:
554 def preexec_fn():
555 try:
556 import resource
557 maxmemory = int(case.maxmemory * 1048576)
558 resource.setrlimit(resource.RLIMIT_AS, (maxmemory, maxmemory))
559 # I would also set a CPU time limit but I do not want the time
560 # that passes between the calls to fork and exec to be counted in
561 except MemoryError:
562 # We do not have enough memory for ourselves;
563 # let the parent know about this
564 raise
565 except Exception:
566 # Well, at least we tried
567 pass
568 case.open_infile() 268 case.open_infile()
569 case.time_started = None 269 case.time_started = None
570 global last_rusage
571 last_rusage = None
572 if case.problem.config.stdio: 270 if case.problem.config.stdio:
573 if options.erase and not case.validator or not case.problem.config.inname: 271 if options.erase and not case.validator or not case.problem.config.inname:
574 # TODO: re-use the same file name if possible 272 # TODO: re-use the same file name if possible
575 # FIXME: 2.5 lacks the delete parameter 273 # FIXME: 2.5 lacks the delete parameter
576 with tempfile.NamedTemporaryFile(delete=False) as f: 274 with tempfile.NamedTemporaryFile(delete=False) as f:
580 inputdatafname = case.problem.config.inname 278 inputdatafname = case.problem.config.inname
581 contextmgr = Copying(case.infile, inputdatafname) 279 contextmgr = Copying(case.infile, inputdatafname)
582 with contextmgr: 280 with contextmgr:
583 with open(inputdatafname) as infile: 281 with open(inputdatafname) as infile:
584 with tempfile.TemporaryFile('w+') if options.erase and not case.validator else open(case.problem.config.outname, 'w+') as outfile: 282 with tempfile.TemporaryFile('w+') if options.erase and not case.validator else open(case.problem.config.outname, 'w+') as outfile:
585 if call is not None: 283 call(case.problem.config.path, case=case, stdin=infile, stdout=outfile, stderr=devnull, universal_newlines=True, bufsize=-1)
586 call(case.problem.config.path, case=case, stdin=infile, stdout=outfile, stderr=devnull, universal_newlines=True, bufsize=-1, preexec_fn=preexec_fn)
587 else:
588 try:
589 try:
590 case.process = Popen(case.problem.config.path, stdin=infile, stdout=outfile, stderr=devnull, universal_newlines=True, bufsize=-1, preexec_fn=preexec_fn)
591 except MemoryError:
592 # If there is not enough memory for the forked test.py,
593 # opt for silent dropping of the limit
594 # TODO: show a warning somewhere
595 case.process = Popen(case.problem.config.path, stdin=infile, stdout=outfile, stderr=devnull, universal_newlines=True, bufsize=-1)
596 except OSError:
597 raise CannotStartTestee(sys.exc_info()[1])
598 case.time_started = clock()
599 time_next_check = case.time_started + .15
600 if not case.maxtime:
601 while True:
602 exitcode, now = case.process.poll(), clock()
603 if exitcode is not None:
604 case.time_stopped = now
605 break
606 # For some reason (probably Microsoft's fault),
607 # msvcrt.kbhit() is slow as hell
608 else:
609 if now >= time_next_check:
610 if canceled():
611 raise CanceledByUser
612 else:
613 time_next_check = now + .15
614 time.sleep(.001)
615 else:
616 time_end = case.time_started + case.maxtime
617 while True:
618 exitcode, now = case.process.poll(), clock()
619 if exitcode is not None:
620 case.time_stopped = now
621 break
622 elif now >= time_end:
623 raise TimeLimitExceeded
624 else:
625 if now >= time_next_check:
626 if canceled():
627 raise CanceledByUser
628 else:
629 time_next_check = now + .15
630 time.sleep(.001)
631 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0: 284 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0:
632 raise NonZeroExitCode(case.process.returncode) 285 raise NonZeroExitCode(case.process.returncode)
633 if case.maxmemory and last_rusage and last_rusage.ru_maxrss > case.maxmemory * (1024 if sys.platform != 'darwin' else 1048576):
634 raise MemoryLimitExceeded
635 callback() 286 callback()
636 case.has_called_back = True 287 case.has_called_back = True
637 outfile.seek(0) 288 outfile.seek(0)
638 return case.validate(outfile) 289 return case.validate(outfile)
639 else: 290 else:
640 case.infile.copy(case.problem.config.inname) 291 case.infile.copy(case.problem.config.inname)
641 if call is not None: 292 call(case.problem.config.path, case=case, stdin=devnull, stdout=devnull, stderr=STDOUT)
642 call(case.problem.config.path, case=case, stdin=devnull, stdout=devnull, stderr=STDOUT, preexec_fn=preexec_fn)
643 else:
644 try:
645 try:
646 case.process = Popen(case.problem.config.path, stdin=devnull, stdout=devnull, stderr=STDOUT, preexec_fn=preexec_fn)
647 except MemoryError:
648 # If there is not enough memory for the forked test.py,
649 # opt for silent dropping of the limit
650 # TODO: show a warning somewhere
651 case.process = Popen(case.problem.config.path, stdin=devnull, stdout=devnull, stderr=STDOUT)
652 except OSError:
653 raise CannotStartTestee(sys.exc_info()[1])
654 case.time_started = clock()
655 time_next_check = case.time_started + .15
656 if not case.maxtime:
657 while True:
658 exitcode, now = case.process.poll(), clock()
659 if exitcode is not None:
660 case.time_stopped = now
661 break
662 else:
663 if now >= time_next_check:
664 if canceled():
665 raise CanceledByUser
666 else:
667 time_next_check = now + .15
668 time.sleep(.001)
669 else:
670 time_end = case.time_started + case.maxtime
671 while True:
672 exitcode, now = case.process.poll(), clock()
673 if exitcode is not None:
674 case.time_stopped = now
675 break
676 elif now >= time_end:
677 raise TimeLimitExceeded
678 else:
679 if now >= time_next_check:
680 if canceled():
681 raise CanceledByUser
682 else:
683 time_next_check = now + .15
684 time.sleep(.001)
685 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0: 293 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0:
686 raise NonZeroExitCode(case.process.returncode) 294 raise NonZeroExitCode(case.process.returncode)
687 if case.maxmemory and last_rusage and last_rusage.ru_maxrss > case.maxmemory * (1024 if sys.platform != 'darwin' else 1048576):
688 raise MemoryLimitExceeded
689 callback() 295 callback()
690 case.has_called_back = True 296 case.has_called_back = True
691 with open(case.problem.config.outname, 'rU') as output: 297 with open(case.problem.config.outname, 'rU') as output:
692 return case.validate(output) 298 return case.validate(output)
693 299