Chromium Code Reviews| Index: subprocess2.py |
| diff --git a/subprocess2.py b/subprocess2.py |
| index e81798613cf6946789841df1dd4fce44417d9897..f5363db197c250c1b1d1a08d806ee9dfc7b6abaa 100644 |
| --- a/subprocess2.py |
| +++ b/subprocess2.py |
| @@ -132,6 +132,42 @@ def get_english_env(env): |
| return env |
| +class NagTimer(object): |
| + """ |
| + Triggers a callback when a time interval passes without an event being fired. |
| + |
| + For example, the event could be receiving terminal output from a subprocess; |
| + and the callback could print a warning to stderr that the subprocess appeared |
| + to be hung. |
| + """ |
| + def __init__(self, interval, cb): |
| + self.interval = interval |
| + self.cb = cb |
| + self.timer = threading.Timer(self.interval, self.fn) |
| + self.last_output = self.previous_last_output = 0 |
| + |
| + def start(self): |
| + self.last_output = self.previous_last_output = time.time() |
| + self.timer.start() |
| + |
| + def event(self): |
| + self.last_output = time.time() |
| + |
| + def fn(self): |
| + now = time.time() |
| + if self.last_output == self.previous_last_output: |
| + self.cb(now - self.previous_last_output) |
| + # Use 0.1 fudge factor, just in case |
| + # (self.last_output - now) is very close to zero. |
| + sleep_time = (self.last_output - now - 0.1) % self.interval |
|
M-A Ruel
2013/05/03 16:42:58
% with float? You should use an explicit cast in t
szager1
2013/05/03 18:16:58
Can you explain why? I can't think of a good reas
M-A Ruel
2013/05/03 18:32:34
Ah ok I hadn't realized that.
|
| + self.previous_last_output = self.last_output |
| + self.timer = threading.Timer(sleep_time + 0.1, self.fn) |
| + self.timer.start() |
| + |
| + def cancel(self): |
| + self.timer.cancel() |
| + |
| + |
| class Popen(subprocess.Popen): |
| """Wraps subprocess.Popen() with various workarounds. |
| @@ -230,8 +266,7 @@ class Popen(subprocess.Popen): |
| # because of memory exhaustion. |
| queue = Queue.Queue() |
| done = threading.Event() |
| - timer = [] |
| - last_output = [time.time()] * 2 |
| + nag = None |
| def write_stdin(): |
| try: |
| @@ -253,28 +288,12 @@ class Popen(subprocess.Popen): |
| data = pipe.read(1) |
| if not data: |
| break |
| - last_output[0] = time.time() |
| + if nag: |
| + nag.event() |
| queue.put((name, data)) |
| finally: |
| queue.put(name) |
| - def nag_fn(): |
| - now = time.time() |
| - if done.is_set(): |
| - return |
| - if last_output[0] == last_output[1]: |
| - logging.warn(' No output for %.0f seconds from command:' % ( |
| - now - last_output[1])) |
| - logging.warn(' %s' % self.cmd_str) |
| - # Use 0.1 fudge factor in case: |
| - # now ~= last_output[0] + self.nag_timer |
| - sleep_time = self.nag_timer + last_output[0] - now - 0.1 |
| - while sleep_time < 0: |
| - sleep_time += self.nag_timer |
| - last_output[1] = last_output[0] |
| - timer[0] = threading.Timer(sleep_time, nag_fn) |
| - timer[0].start() |
| - |
| def timeout_fn(): |
| try: |
| done.wait(self.timeout) |
| @@ -313,8 +332,11 @@ class Popen(subprocess.Popen): |
| t.start() |
| if self.nag_timer: |
| - timer.append(threading.Timer(self.nag_timer, nag_fn)) |
| - timer[0].start() |
| + def _nag_cb(elapsed): |
| + logging.warn(' No output for %.0f seconds from command:' % elapsed) |
| + logging.warn(' %s' % self.cmd_str) |
| + nag = NagTimer(self.nag_timer, _nag_cb) |
| + nag.start() |
| timed_out = False |
| try: |
| @@ -339,8 +361,8 @@ class Popen(subprocess.Popen): |
| finally: |
| # Stop the threads. |
| done.set() |
| - if timer: |
| - timer[0].cancel() |
| + if nag: |
| + nag.cancel() |
| if 'wait' in threads: |
| # Accelerate things, otherwise it would hang until the child process is |
| # done. |