Mercurial > ~astiob > upreckon > hgweb
comparison unix.py @ 118:16fe21d6582e
Fixed a few race conditions in unix.call triggered by very fast testees
Effects included misrecording of wall-clock time usage and crashes.
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Tue, 12 Apr 2011 22:25:18 +0300 |
parents | 218b8c28549c |
children | 0b265fe9c81f |
comparison
equal
deleted
inserted
replaced
117:6bb59a011bcb | 118:16fe21d6582e |
---|---|
43 tty.setcbreak(sys.stdin.fileno()) | 43 tty.setcbreak(sys.stdin.fileno()) |
44 def pause(): | 44 def pause(): |
45 sys.stdin.read(1) | 45 sys.stdin.read(1) |
46 | 46 |
47 try: | 47 try: |
48 from signal import SIGCHLD, signal, SIG_DFL | 48 from signal import SIGCHLD, SIG_DFL, signal, set_wakeup_fd |
49 from select import select, error as SelectError | 49 from select import select, error as SelectError |
50 from errno import EINTR | 50 from errno import EAGAIN, EINTR |
51 from fcntl import fcntl, F_SETFD, F_GETFD | 51 from fcntl import fcntl, F_SETFD, F_GETFD, F_SETFL, F_GETFL |
52 from os import O_NONBLOCK | |
52 try: | 53 try: |
53 import cPickle as pickle | 54 import cPickle as pickle |
54 except ImportError: | 55 except ImportError: |
55 import pickle | 56 import pickle |
56 except ImportError: | 57 except ImportError: |
104 from resource import RLIMIT_VMEM | 105 from resource import RLIMIT_VMEM |
105 except ImportError: | 106 except ImportError: |
106 setrlimit = None | 107 setrlimit = None |
107 | 108 |
108 # Make SIGCHLD interrupt sleep() and select() | 109 # Make SIGCHLD interrupt sleep() and select() |
110 sigchld_pipe_read, sigchld_pipe_write = os.pipe() | |
111 fcntl(sigchld_pipe_read, F_SETFL, | |
112 fcntl(sigchld_pipe_read, F_GETFL) | O_NONBLOCK) | |
113 fcntl(sigchld_pipe_write, F_SETFL, | |
114 fcntl(sigchld_pipe_write, F_GETFL) | O_NONBLOCK) | |
109 def bury_child(signum, frame): | 115 def bury_child(signum, frame): |
110 try: | 116 try: |
111 bury_child.case.time_stopped = clock() | 117 bury_child.case.time_stopped = clock() |
112 except Exception: | 118 except Exception: |
113 pass | 119 pass |
114 signal(SIGCHLD, bury_child) | 120 signal(SIGCHLD, bury_child) |
121 set_wakeup_fd(sigchld_pipe_write) | |
115 class SignalIgnorer(object): | 122 class SignalIgnorer(object): |
116 def __enter__(self): | 123 def __enter__(self): |
117 signal(SIGCHLD, SIG_DFL) | 124 signal(SIGCHLD, SIG_DFL) |
118 def __exit__(self, exc_type, exc_value, traceback): | 125 def __exit__(self, exc_type, exc_value, traceback): |
119 signal(SIGCHLD, bury_child) | 126 signal(SIGCHLD, bury_child) |
135 # passing between the calls to fork and exec to be counted in | 142 # passing between the calls to fork and exec to be counted in |
136 os.write(write, pickle.dumps((clock(), cpuclock()), 1)) | 143 os.write(write, pickle.dumps((clock(), cpuclock()), 1)) |
137 kwargs['preexec_fn'] = preexec_fn | 144 kwargs['preexec_fn'] = preexec_fn |
138 old_rusage = getrusage(RUSAGE_CHILDREN) | 145 old_rusage = getrusage(RUSAGE_CHILDREN) |
139 last_rusage = None | 146 last_rusage = None |
147 while True: | |
148 try: | |
149 os.read(sigchld_pipe_read, 512) | |
150 except OSError: | |
151 if sys.exc_info()[1].errno == EAGAIN: | |
152 break | |
153 else: | |
154 raise | |
140 try: | 155 try: |
141 case.process = Popen(*args, **kwargs) | 156 case.process = Popen(*args, **kwargs) |
142 except OSError: | 157 except OSError: |
143 os.close(read) | 158 os.close(read) |
144 raise testcases.CannotStartTestee(sys.exc_info()[1]) | 159 raise testcases.CannotStartTestee(sys.exc_info()[1]) |
145 finally: | 160 finally: |
146 os.close(write) | 161 os.close(write) |
147 try: | 162 try: |
148 if not catch_escape: | 163 if not catch_escape: |
149 if case.maxwalltime: | 164 if case.maxwalltime: |
150 time.sleep(case.maxwalltime) | 165 try: |
166 select((sigchld_pipe_read,), (), (), case.maxwalltime) | |
167 except SelectError: | |
168 if sys.exc_info()[1].args[0] != EINTR: | |
169 raise | |
151 if case.process.poll() is None: | 170 if case.process.poll() is None: |
152 raise testcases.WallTimeLimitExceeded | 171 raise testcases.WallTimeLimitExceeded |
153 else: | 172 else: |
154 case.process.wait() | 173 case.process.wait() |
155 else: | 174 else: |
156 if not case.maxwalltime: | 175 if not case.maxwalltime: |
157 try: | 176 try: |
158 while case.process.poll() is None: | 177 while case.process.poll() is None: |
159 if select((sys.stdin,), (), ())[0]: | 178 s = select((sys.stdin, sigchld_pipe_read), (), ()) |
160 if sys.stdin.read(1) == '\33': | 179 if (sigchld_pipe_read not in s[0] and |
161 raise testcases.CanceledByUser | 180 sys.stdin.read(1) == '\33'): |
162 except SelectError: | 181 raise testcases.CanceledByUser |
182 except (SelectError, IOError): | |
163 if sys.exc_info()[1].args[0] != EINTR: | 183 if sys.exc_info()[1].args[0] != EINTR: |
164 raise | 184 raise |
165 else: | 185 else: |
166 case.process.poll() | 186 case.process.poll() |
167 else: | 187 else: |
168 time_end = clock() + case.maxwalltime | 188 time_end = clock() + case.maxwalltime |
169 try: | 189 try: |
170 while case.process.poll() is None: | 190 while case.process.poll() is None: |
171 remaining = time_end - clock() | 191 remaining = time_end - clock() |
172 if remaining > 0: | 192 if remaining > 0: |
173 if select((sys.stdin,), (), (), remaining)[0]: | 193 s = select((sys.stdin, sigchld_pipe_read), |
174 if sys.stdin.read(1) == '\33': | 194 (), (), remaining) |
175 raise testcases.CanceledByUser | 195 if (sigchld_pipe_read not in s[0] and |
196 sys.stdin.read(1) == '\33'): | |
197 raise testcases.CanceledByUser | |
176 else: | 198 else: |
177 raise testcases.WallTimeLimitExceeded | 199 raise testcases.WallTimeLimitExceeded |
178 except SelectError: | 200 except (SelectError, IOError): |
179 if sys.exc_info()[1].args[0] != EINTR: | 201 if sys.exc_info()[1].args[0] != EINTR: |
180 raise | 202 raise |
181 else: | 203 else: |
182 case.process.poll() | 204 case.process.poll() |
183 finally: | 205 finally: |