Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(67)

Side by Side Diff: gclient_utils.py

Issue 2410853002: Revert of gclient: kill git fetch operation that hangs. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « gclient_scm.py ('k') | tests/gclient_scm_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Generic utils.""" 5 """Generic utils."""
6 6
7 import codecs 7 import codecs
8 import cStringIO 8 import cStringIO
9 import datetime 9 import datetime
10 import logging 10 import logging
(...skipping 441 matching lines...) Expand 10 before | Expand all | Expand 10 after
452 time.sleep(0.5) 452 time.sleep(0.5)
453 GClientChildren._attemptToKillChildren() 453 GClientChildren._attemptToKillChildren()
454 454
455 with GCLIENT_CHILDREN_LOCK: 455 with GCLIENT_CHILDREN_LOCK:
456 if GCLIENT_CHILDREN: 456 if GCLIENT_CHILDREN:
457 print >> sys.stderr, 'Could not kill the following subprocesses:' 457 print >> sys.stderr, 'Could not kill the following subprocesses:'
458 for zombie in GCLIENT_CHILDREN: 458 for zombie in GCLIENT_CHILDREN:
459 print >> sys.stderr, ' ', zombie.pid 459 print >> sys.stderr, ' ', zombie.pid
460 460
461 461
462 class _KillTimer(object):
463 """Timer that kills child process after certain interval since last poke or
464 creation.
465 """
466 # TODO(tandrii): we really want to make use of subprocess42 here, and not
467 # re-invent the wheel, but it's too much work :(
468
469 def __init__(self, timeout, child):
470 self._timeout = timeout
471 self._child = child
472
473 self._cv = threading.Condition()
474 # All items below are protected by condition above.
475 self._kill_at = None
476 self._working = True
477 self._thread = None
478
479 # Start the timer immediately.
480 if self._timeout:
481 self._kill_at = time.time() + self._timeout
482 self._thread = threading.Thread(name='_KillTimer', target=self._work)
483 self._thread.daemon = True
484 self._thread.start()
485
486 def poke(self):
487 if not self._timeout:
488 return
489 with self._cv:
490 self._kill_at = time.time() + self._timeout
491
492 def cancel(self):
493 with self._cv:
494 self._working = False
495 self._cv.notifyAll()
496
497 def _work(self):
498 if not self._timeout:
499 return
500 while True:
501 with self._cv:
502 if not self._working:
503 return
504 left = self._kill_at - time.time()
505 if left > 0:
506 self._cv.wait(timeout=left)
507 continue
508 try:
509 logging.warn('killing child %s because of no output for %fs',
510 self._child, self._timeout)
511 self._child.kill()
512 except OSError:
513 logging.exception('failed to kill child %s', self._child)
514 return
515
516
517 def CheckCallAndFilter(args, stdout=None, filter_fn=None, 462 def CheckCallAndFilter(args, stdout=None, filter_fn=None,
518 print_stdout=None, call_filter_on_first_line=False, 463 print_stdout=None, call_filter_on_first_line=False,
519 retry=False, kill_timeout=None, **kwargs): 464 retry=False, **kwargs):
520 """Runs a command and calls back a filter function if needed. 465 """Runs a command and calls back a filter function if needed.
521 466
522 Accepts all subprocess2.Popen() parameters plus: 467 Accepts all subprocess2.Popen() parameters plus:
523 print_stdout: If True, the command's stdout is forwarded to stdout. 468 print_stdout: If True, the command's stdout is forwarded to stdout.
524 filter_fn: A function taking a single string argument called with each line 469 filter_fn: A function taking a single string argument called with each line
525 of the subprocess2's output. Each line has the trailing newline 470 of the subprocess2's output. Each line has the trailing newline
526 character trimmed. 471 character trimmed.
527 stdout: Can be any bufferable output. 472 stdout: Can be any bufferable output.
528 retry: If the process exits non-zero, sleep for a brief interval and try 473 retry: If the process exits non-zero, sleep for a brief interval and try
529 again, up to RETRY_MAX times. 474 again, up to RETRY_MAX times.
530 kill_timeout: (float) if given, number of seconds after which process would
531 be killed if there is no output. Must not be used with shell=True as
532 only shell process would be killed, but not processes spawned by
533 shell.
534 475
535 stderr is always redirected to stdout. 476 stderr is always redirected to stdout.
536 """ 477 """
537 assert print_stdout or filter_fn 478 assert print_stdout or filter_fn
538 assert not kwargs.get('shell', False) or not kill_timeout, (
539 'kill_timeout should not be used with shell=True')
540 stdout = stdout or sys.stdout 479 stdout = stdout or sys.stdout
541 output = cStringIO.StringIO() 480 output = cStringIO.StringIO()
542 filter_fn = filter_fn or (lambda x: None) 481 filter_fn = filter_fn or (lambda x: None)
543 482
544 sleep_interval = RETRY_INITIAL_SLEEP 483 sleep_interval = RETRY_INITIAL_SLEEP
545 run_cwd = kwargs.get('cwd', os.getcwd()) 484 run_cwd = kwargs.get('cwd', os.getcwd())
546 for _ in xrange(RETRY_MAX + 1): 485 for _ in xrange(RETRY_MAX + 1):
547 kid = subprocess2.Popen( 486 kid = subprocess2.Popen(
548 args, bufsize=0, stdout=subprocess2.PIPE, stderr=subprocess2.STDOUT, 487 args, bufsize=0, stdout=subprocess2.PIPE, stderr=subprocess2.STDOUT,
549 **kwargs) 488 **kwargs)
550 489
551 GClientChildren.add(kid) 490 GClientChildren.add(kid)
552 491
553 # Do a flush of stdout before we begin reading from the subprocess2's stdout 492 # Do a flush of stdout before we begin reading from the subprocess2's stdout
554 stdout.flush() 493 stdout.flush()
555 494
556 # Also, we need to forward stdout to prevent weird re-ordering of output. 495 # Also, we need to forward stdout to prevent weird re-ordering of output.
557 # This has to be done on a per byte basis to make sure it is not buffered: 496 # This has to be done on a per byte basis to make sure it is not buffered:
558 # normally buffering is done for each line, but if svn requests input, no 497 # normally buffering is done for each line, but if svn requests input, no
559 # end-of-line character is output after the prompt and it would not show up. 498 # end-of-line character is output after the prompt and it would not show up.
560 try: 499 try:
561 timeout_killer = _KillTimer(kill_timeout, kid)
562 in_byte = kid.stdout.read(1) 500 in_byte = kid.stdout.read(1)
563 if in_byte: 501 if in_byte:
564 if call_filter_on_first_line: 502 if call_filter_on_first_line:
565 filter_fn(None) 503 filter_fn(None)
566 in_line = '' 504 in_line = ''
567 while in_byte: 505 while in_byte:
568 timeout_killer.poke()
569 output.write(in_byte) 506 output.write(in_byte)
570 if print_stdout: 507 if print_stdout:
571 stdout.write(in_byte) 508 stdout.write(in_byte)
572 if in_byte not in ['\r', '\n']: 509 if in_byte not in ['\r', '\n']:
573 in_line += in_byte 510 in_line += in_byte
574 else: 511 else:
575 filter_fn(in_line) 512 filter_fn(in_line)
576 in_line = '' 513 in_line = ''
577 in_byte = kid.stdout.read(1) 514 in_byte = kid.stdout.read(1)
578 # Flush the rest of buffered output. This is only an issue with 515 # Flush the rest of buffered output. This is only an issue with
579 # stdout/stderr not ending with a \n. 516 # stdout/stderr not ending with a \n.
580 if len(in_line): 517 if len(in_line):
581 filter_fn(in_line) 518 filter_fn(in_line)
582 rv = kid.wait() 519 rv = kid.wait()
583 timeout_killer.cancel()
584 520
585 # Don't put this in a 'finally,' since the child may still run if we get 521 # Don't put this in a 'finally,' since the child may still run if we get
586 # an exception. 522 # an exception.
587 GClientChildren.remove(kid) 523 GClientChildren.remove(kid)
588 524
589 except KeyboardInterrupt: 525 except KeyboardInterrupt:
590 print >> sys.stderr, 'Failed while running "%s"' % ' '.join(args) 526 print >> sys.stderr, 'Failed while running "%s"' % ' '.join(args)
591 raise 527 raise
592 528
593 if rv == 0: 529 if rv == 0:
(...skipping 695 matching lines...) Expand 10 before | Expand all | Expand 10 after
1289 # Just incase we have some ~/blah paths. 1225 # Just incase we have some ~/blah paths.
1290 target = os.path.abspath(os.path.expanduser(target)) 1226 target = os.path.abspath(os.path.expanduser(target))
1291 if os.path.isfile(target) and os.access(target, os.X_OK): 1227 if os.path.isfile(target) and os.access(target, os.X_OK):
1292 return target 1228 return target
1293 if sys.platform.startswith('win'): 1229 if sys.platform.startswith('win'):
1294 for suffix in ('.bat', '.cmd', '.exe'): 1230 for suffix in ('.bat', '.cmd', '.exe'):
1295 alt_target = target + suffix 1231 alt_target = target + suffix
1296 if os.path.isfile(alt_target) and os.access(alt_target, os.X_OK): 1232 if os.path.isfile(alt_target) and os.access(alt_target, os.X_OK):
1297 return alt_target 1233 return alt_target
1298 return None 1234 return None
OLDNEW
« no previous file with comments | « gclient_scm.py ('k') | tests/gclient_scm_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698