| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -459,9 +459,64 @@ class GClientChildren(object):
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          print >> sys.stderr, '  ', zombie.pid
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				class _KillTimer(object):
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  """Timer that kills child process after certain interval since last poke or
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  creation.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  """
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  # TODO(tandrii): we really want to make use of subprocess42 here, and not
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  # re-invent the wheel, but it's too much work :(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  def __init__(self, timeout, child):
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    self._timeout = timeout
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    self._child = child
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    self._cv = threading.Condition()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    # All items below are protected by condition above.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    self._kill_at = None
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    self._working = True
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    self._thread = None
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    # Start the timer immediately.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if self._timeout:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      self._kill_at = time.time() + self._timeout
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      self._thread = threading.Thread(name='_KillTimer', target=self._work)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      self._thread.daemon = True
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      self._thread.start()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  def poke(self):
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if not self._timeout:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    with self._cv:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      self._kill_at = time.time() + self._timeout
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  def cancel(self):
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    with self._cv:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      self._working = False
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      self._cv.notifyAll()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  def _work(self):
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if not self._timeout:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    while True:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      with self._cv:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if not self._working:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        left = self._kill_at - time.time()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if left > 0:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          self._cv.wait(timeout=left)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          continue
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        try:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          logging.warn('killing child %s because of no output for %fs',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                       self._child, self._timeout)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          self._child.kill()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        except OSError:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          logging.exception('failed to kill child %s', self._child)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				def CheckCallAndFilter(args, stdout=None, filter_fn=None,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                       print_stdout=None, call_filter_on_first_line=False,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                       retry=False, **kwargs):
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                       retry=False, kill_timeout=None, **kwargs):
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  """Runs a command and calls back a filter function if needed.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  Accepts all subprocess2.Popen() parameters plus:
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -472,10 +527,16 @@ def CheckCallAndFilter(args, stdout=None, filter_fn=None,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    stdout: Can be any bufferable output.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    retry: If the process exits non-zero, sleep for a brief interval and try
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				           again, up to RETRY_MAX times.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    kill_timeout: (float) if given, number of seconds after which process would
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				           be killed if there is no output. Must not be used with shell=True as
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				           only shell process would be killed, but not processes spawned by
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				           shell.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  stderr is always redirected to stdout.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  """
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  assert print_stdout or filter_fn
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  assert not kwargs.get('shell', False) or not kill_timeout, (
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      'kill_timeout should not be used with shell=True')
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  stdout = stdout or sys.stdout
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  output = cStringIO.StringIO()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  filter_fn = filter_fn or (lambda x: None)
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -497,12 +558,14 @@ def CheckCallAndFilter(args, stdout=None, filter_fn=None,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    # normally buffering is done for each line, but if svn requests input, no
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    # end-of-line character is output after the prompt and it would not show up.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    try:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      timeout_killer = _KillTimer(kill_timeout, kid)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      in_byte = kid.stdout.read(1)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if in_byte:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if call_filter_on_first_line:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          filter_fn(None)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        in_line = ''
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        while in_byte:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          timeout_killer.poke()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          output.write(in_byte)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          if print_stdout:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            stdout.write(in_byte)
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -517,6 +580,7 @@ def CheckCallAndFilter(args, stdout=None, filter_fn=None,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if len(in_line):
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          filter_fn(in_line)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      rv = kid.wait()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      timeout_killer.cancel()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      # Don't put this in a 'finally,' since the child may still run if we get
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      # an exception.
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |