comparison win32.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 741ae3391b61
comparison
equal deleted inserted replaced
81:24752db487c5 82:06356af50bf9
1 # Copyright (c) 2011 Chortos-2 <chortos@inbox.lv> 1 # Copyright (c) 2010-2011 Chortos-2 <chortos@inbox.lv>
2 2
3 from __future__ import division, with_statement 3 from __future__ import division, with_statement
4 import sys
4 5
5 try: 6 try:
6 from compat import * 7 from compat import *
7 from testcases import (TimeLimitExceeded, MemoryLimitExceeded, 8 import testcases # mutual import
8 CanceledByUser, CannotStartTestee)
9 except ImportError: 9 except ImportError:
10 import __main__ 10 import __main__
11 __main__.import_error(sys.exc_info()[1]) 11 __main__.import_error(sys.exc_info()[1])
12 12
13 from __main__ import clock
14 from ctypes import * 13 from ctypes import *
15 from ctypes.wintypes import * 14 from ctypes.wintypes import *
15 from msvcrt import getch as pause
16 from subprocess import Popen 16 from subprocess import Popen
17 from __main__ import clock
17 18
18 # Defaults that may be overwritten by values from _subprocess 19 # Defaults that may be overwritten by values from _subprocess
19 INFINITE = -1 20 INFINITE = -1
20 STD_INPUT_HANDLE = -10 21 STD_INPUT_HANDLE = -10
21 WAIT_OBJECT_0 = 0 22 WAIT_OBJECT_0 = 0
40 __getnewargs__ = lambda self: tuple(self) 41 __getnewargs__ = lambda self: tuple(self)
41 kernel, user = (property(itemgetter(i)) for i in (0, 1)) 42 kernel, user = (property(itemgetter(i)) for i in (0, 1))
42 else: 43 else:
43 ProcessTimes = namedtuple('ProcessTimes', 'kernel user') 44 ProcessTimes = namedtuple('ProcessTimes', 'kernel user')
44 45
45 __all__ = 'call', 'kill', 'terminate' 46 __all__ = 'call', 'kill', 'terminate', 'pause'
46 47
47 48
48 # Automatically convert _subprocess handle objects into low-level HANDLEs 49 # Automatically convert _subprocess handle objects into low-level HANDLEs
49 # and replicate their functionality for our own use 50 # and replicate their functionality for our own use
50 try: 51 try:
313 ('ProcessMemoryLimit', SIZE_T), 314 ('ProcessMemoryLimit', SIZE_T),
314 ('JobMemoryLimit', SIZE_T), 315 ('JobMemoryLimit', SIZE_T),
315 ('PeakProcessMemoryUsed', SIZE_T), 316 ('PeakProcessMemoryUsed', SIZE_T),
316 ('PeakJobMemoryUsed', SIZE_T)) 317 ('PeakJobMemoryUsed', SIZE_T))
317 318
318 prototype = WINFUNCTYPE(BOOL, HANDLE, c_int, c_void_p, DWORD) 319 prototype = WINFUNCTYPE(BOOL, Handle, c_int, c_void_p, DWORD)
319 flags = (1, 'job'), (1, 'infoclass'), (1, 'info'), (1, 'infosize') 320 flags = (1, 'job'), (1, 'infoclass'), (1, 'info'), (1, 'infosize')
320 try: 321 try:
321 _setjobinfo = prototype(('SetInformationJobObject',windll.kernel32), flags) 322 _setjobinfo = prototype(('SetInformationJobObject',windll.kernel32), flags)
322 except AttributeError: 323 except AttributeError:
323 # Available on 2000 and up, NT line only 324 # Available on 2000 and up, NT line only
325 else: 326 else:
326 def errcheck(result, func, args): 327 def errcheck(result, func, args):
327 if not result: raise WinError() 328 if not result: raise WinError()
328 _setjobinfo.errcheck = errcheck 329 _setjobinfo.errcheck = errcheck
329 def SetInformationJobObject(job, infoclass, info): 330 def SetInformationJobObject(job, infoclass, info):
330 return _setjobinfo(job, infoclass, info, sizeof(info)) 331 return _setjobinfo(job, infoclass, byref(info), sizeof(info))
331 332
332 ( 333 (
333 JobObjectBasicAccountingInformation, 334 JobObjectBasicAccountingInformation,
334 JobObjectBasicLimitInformation, 335 JobObjectBasicLimitInformation,
335 JobObjectBasicProcessIdList, 336 JobObjectBasicProcessIdList,
414 except WindowsError: 415 except WindowsError:
415 console_input = False 416 console_input = False
416 else: 417 else:
417 console_input = True 418 console_input = True
418 FlushConsoleInputBuffer(stdin) 419 FlushConsoleInputBuffer(stdin)
419
420 def kill(process):
421 try:
422 process.terminate()
423 except AttributeError:
424 TerminateProcess(process._handle)
425 terminate = kill
426 420
427 def call(*args, **kwargs): 421 def call(*args, **kwargs):
428 case = kwargs.pop('case') 422 case = kwargs.pop('case')
429 job = CreateJobObject(None) 423 job = CreateJobObject(None)
430 flags = 0 424 flags = 0
441 ) 435 )
442 SetInformationJobObject(job, JobObjectExtendedLimitInformation, limits) 436 SetInformationJobObject(job, JobObjectExtendedLimitInformation, limits)
443 try: 437 try:
444 case.process = Popen(*args, **kwargs) 438 case.process = Popen(*args, **kwargs)
445 except OSError: 439 except OSError:
446 raise CannotStartTestee(sys.exc_info()[1]) 440 raise testcases.CannotStartTestee(sys.exc_info()[1])
447 case.time_started = clock() 441 case.time_started = clock()
448 AssignProcessToJobObject(job, case.process._handle) 442 AssignProcessToJobObject(job, case.process._handle)
449 if not console_input: 443 if not console_input:
450 if case.maxwalltime: 444 if case.maxwalltime:
451 if (WaitForSingleObject(case.process._handle, case.maxwalltime) != 445 if (WaitForSingleObject(case.process._handle, case.maxwalltime) !=
452 WAIT_OBJECT_0): 446 WAIT_OBJECT_0):
453 raise TimeLimitExceeded 447 raise testcases.WallTimeLimitExceeded
454 else: 448 else:
455 case.process.wait() 449 case.process.wait()
456 else: 450 else:
457 handles = stdin, case.process._handle 451 handles = stdin, case.process._handle
458 if case.maxwalltime: 452 if case.maxwalltime:
465 ir = ReadConsoleInput(stdin) 459 ir = ReadConsoleInput(stdin)
466 if (ir and 460 if (ir and
467 ir.EventType == 1 and 461 ir.EventType == 1 and
468 ir.Event.KeyEvent.bKeyDown and 462 ir.Event.KeyEvent.bKeyDown and
469 ir.Event.KeyEvent.wVirtualKeyCode == 27): 463 ir.Event.KeyEvent.wVirtualKeyCode == 27):
470 raise CanceledByUser 464 raise testcases.CanceledByUser
471 else: 465 else:
472 raise TimeLimitExceeded 466 raise testcases.WallTimeLimitExceeded
473 else: 467 else:
474 while case.process.poll() is None: 468 while case.process.poll() is None:
475 if (WaitForMultipleObjects(handles, False, INFINITE) == 469 if (WaitForMultipleObjects(handles, False, INFINITE) ==
476 WAIT_OBJECT_0): 470 WAIT_OBJECT_0):
477 ir = ReadConsoleInput(stdin) 471 ir = ReadConsoleInput(stdin)
478 if (ir and 472 if (ir and
479 ir.EventType == 1 and 473 ir.EventType == 1 and
480 ir.Event.KeyEvent.bKeyDown and 474 ir.Event.KeyEvent.bKeyDown and
481 ir.Event.KeyEvent.wVirtualKeyCode == 27): 475 ir.Event.KeyEvent.wVirtualKeyCode == 27):
482 raise CanceledByUser 476 raise testcases.CanceledByUser
483 case.time_stopped = clock() 477 case.time_stopped = clock()
484 if case.maxcputime and GetProcessTimes: 478 if GetProcessTimes:
485 try: 479 try:
486 times = GetProcessTimes(case.process._handle) 480 times = GetProcessTimes(case.process._handle)
487 except WindowsError: 481 except WindowsError:
488 pass 482 pass
489 else: 483 else:
490 if times.kernel + times.user > case.maxcputime: 484 time = times.kernel + times.user
491 raise TimeLimitExceeded 485 case.time_stopped = time
486 case.time_started = 0
487 case.time_limit_string = case.cpu_time_limit_string
488 if case.maxcputime and time > case.maxcputime:
489 raise testcases.CPUTimeLimitExceeded
490 if case.maxcputime and case.process.returncode == 1816:
491 raise testcases.CPUTimeLimitExceeded
492 if case.maxmemory and case.process.returncode == -0x3ffffffb:
493 raise testcases.MemoryLimitExceeded
492 if case.maxmemory and GetProcessMemoryInfo: 494 if case.maxmemory and GetProcessMemoryInfo:
493 try: 495 try:
494 counters = GetProcessMemoryInfo(case.process._handle) 496 counters = GetProcessMemoryInfo(case.process._handle)
495 except WindowsError: 497 except WindowsError:
496 pass 498 pass
497 else: 499 else:
498 if counters.PeakPagefileUsage > case.maxmemory * 1048576: 500 if counters.PeakPagefileUsage > case.maxmemory * 1048576:
499 raise MemoryLimitExceeded 501 raise testcases.MemoryLimitExceeded
502
503
504 def kill(process):
505 try:
506 process.terminate()
507 except AttributeError:
508 TerminateProcess(process._handle)
509 terminate = kill