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