Mercurial > ~astiob > upreckon > hgweb
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__ |
