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__ |