OLD | NEW |
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 logging | 10 import logging |
10 import os | 11 import os |
11 import pipes | 12 import pipes |
12 import platform | 13 import platform |
13 import Queue | 14 import Queue |
14 import re | 15 import re |
15 import stat | 16 import stat |
16 import subprocess | 17 import subprocess |
17 import sys | 18 import sys |
18 import tempfile | 19 import tempfile |
19 import threading | 20 import threading |
20 import time | 21 import time |
21 import urlparse | 22 import urlparse |
22 | 23 |
23 import subprocess2 | 24 import subprocess2 |
24 | 25 |
25 | 26 |
26 RETRY_MAX = 3 | 27 RETRY_MAX = 3 |
27 RETRY_INITIAL_SLEEP = 0.5 | 28 RETRY_INITIAL_SLEEP = 0.5 |
| 29 START = datetime.datetime.now() |
28 | 30 |
29 | 31 |
30 _WARNINGS = [] | 32 _WARNINGS = [] |
31 | 33 |
32 | 34 |
33 # These repos are known to cause OOM errors on 32-bit platforms, due the the | 35 # These repos are known to cause OOM errors on 32-bit platforms, due the the |
34 # very large objects they contain. It is not safe to use threaded index-pack | 36 # very large objects they contain. It is not safe to use threaded index-pack |
35 # when cloning/fetching them. | 37 # when cloning/fetching them. |
36 THREADED_INDEX_PACK_BLACKLIST = [ | 38 THREADED_INDEX_PACK_BLACKLIST = [ |
37 'https://chromium.googlesource.com/chromium/reference_builds/chrome_win.git' | 39 'https://chromium.googlesource.com/chromium/reference_builds/chrome_win.git' |
38 ] | 40 ] |
39 | 41 |
40 | 42 |
41 class Error(Exception): | 43 class Error(Exception): |
42 """gclient exception class.""" | 44 """gclient exception class.""" |
43 def __init__(self, msg, *args, **kwargs): | 45 def __init__(self, msg, *args, **kwargs): |
44 index = getattr(threading.currentThread(), 'index', 0) | 46 index = getattr(threading.currentThread(), 'index', 0) |
45 if index: | 47 if index: |
46 msg = '\n'.join('%d> %s' % (index, l) for l in msg.splitlines()) | 48 msg = '\n'.join('%d> %s' % (index, l) for l in msg.splitlines()) |
47 super(Error, self).__init__(msg, *args, **kwargs) | 49 super(Error, self).__init__(msg, *args, **kwargs) |
48 | 50 |
49 | 51 |
| 52 def Elapsed(until=None): |
| 53 if until is None: |
| 54 until = datetime.datetime.now() |
| 55 return str(until - START).partition('.')[0] |
| 56 |
| 57 |
50 def PrintWarnings(): | 58 def PrintWarnings(): |
51 """Prints any accumulated warnings.""" | 59 """Prints any accumulated warnings.""" |
52 if _WARNINGS: | 60 if _WARNINGS: |
53 print >> sys.stderr, '\n\nWarnings:' | 61 print >> sys.stderr, '\n\nWarnings:' |
54 for warning in _WARNINGS: | 62 for warning in _WARNINGS: |
55 print >> sys.stderr, warning | 63 print >> sys.stderr, warning |
56 | 64 |
57 | 65 |
58 def AddWarning(msg): | 66 def AddWarning(msg): |
59 """Adds the given warning message to the list of accumulated warnings.""" | 67 """Adds the given warning message to the list of accumulated warnings.""" |
(...skipping 416 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
476 try: | 484 try: |
477 in_byte = kid.stdout.read(1) | 485 in_byte = kid.stdout.read(1) |
478 if in_byte: | 486 if in_byte: |
479 if call_filter_on_first_line: | 487 if call_filter_on_first_line: |
480 filter_fn(None) | 488 filter_fn(None) |
481 in_line = '' | 489 in_line = '' |
482 while in_byte: | 490 while in_byte: |
483 output.write(in_byte) | 491 output.write(in_byte) |
484 if print_stdout: | 492 if print_stdout: |
485 stdout.write(in_byte) | 493 stdout.write(in_byte) |
486 if in_byte != '\r': | 494 if in_byte not in ['\r', '\n']: |
487 if in_byte != '\n': | 495 in_line += in_byte |
488 in_line += in_byte | |
489 else: | |
490 filter_fn(in_line) | |
491 in_line = '' | |
492 else: | 496 else: |
493 filter_fn(in_line) | 497 filter_fn(in_line) |
494 in_line = '' | 498 in_line = '' |
495 in_byte = kid.stdout.read(1) | 499 in_byte = kid.stdout.read(1) |
496 # Flush the rest of buffered output. This is only an issue with | 500 # Flush the rest of buffered output. This is only an issue with |
497 # stdout/stderr not ending with a \n. | 501 # stdout/stderr not ending with a \n. |
498 if len(in_line): | 502 if len(in_line): |
499 filter_fn(in_line) | 503 filter_fn(in_line) |
500 rv = kid.wait() | 504 rv = kid.wait() |
501 | 505 |
(...skipping 16 matching lines...) Expand all Loading... |
518 raise subprocess2.CalledProcessError( | 522 raise subprocess2.CalledProcessError( |
519 rv, args, kwargs.get('cwd', None), None, None) | 523 rv, args, kwargs.get('cwd', None), None, None) |
520 | 524 |
521 | 525 |
522 class GitFilter(object): | 526 class GitFilter(object): |
523 """A filter_fn implementation for quieting down git output messages. | 527 """A filter_fn implementation for quieting down git output messages. |
524 | 528 |
525 Allows a custom function to skip certain lines (predicate), and will throttle | 529 Allows a custom function to skip certain lines (predicate), and will throttle |
526 the output of percentage completed lines to only output every X seconds. | 530 the output of percentage completed lines to only output every X seconds. |
527 """ | 531 """ |
528 PERCENT_RE = re.compile('.* ([0-9]{1,2})% .*') | 532 PERCENT_RE = re.compile('(.*) ([0-9]{1,3})% .*') |
529 | 533 |
530 def __init__(self, time_throttle=0, predicate=None): | 534 def __init__(self, time_throttle=0, predicate=None, out_fh=None): |
531 """ | 535 """ |
532 Args: | 536 Args: |
533 time_throttle (int): GitFilter will throttle 'noisy' output (such as the | 537 time_throttle (int): GitFilter will throttle 'noisy' output (such as the |
534 XX% complete messages) to only be printed at least |time_throttle| | 538 XX% complete messages) to only be printed at least |time_throttle| |
535 seconds apart. | 539 seconds apart. |
536 predicate (f(line)): An optional function which is invoked for every line. | 540 predicate (f(line)): An optional function which is invoked for every line. |
537 The line will be skipped if predicate(line) returns False. | 541 The line will be skipped if predicate(line) returns False. |
| 542 out_fh: File handle to write output to. |
538 """ | 543 """ |
539 self.last_time = 0 | 544 self.last_time = 0 |
540 self.time_throttle = time_throttle | 545 self.time_throttle = time_throttle |
541 self.predicate = predicate | 546 self.predicate = predicate |
| 547 self.out_fh = out_fh or sys.stdout |
| 548 self.progress_prefix = None |
542 | 549 |
543 def __call__(self, line): | 550 def __call__(self, line): |
544 # git uses an escape sequence to clear the line; elide it. | 551 # git uses an escape sequence to clear the line; elide it. |
545 esc = line.find(unichr(033)) | 552 esc = line.find(unichr(033)) |
546 if esc > -1: | 553 if esc > -1: |
547 line = line[:esc] | 554 line = line[:esc] |
548 if self.predicate and not self.predicate(line): | 555 if self.predicate and not self.predicate(line): |
549 return | 556 return |
550 now = time.time() | 557 now = time.time() |
551 match = self.PERCENT_RE.match(line) | 558 match = self.PERCENT_RE.match(line) |
552 if not match: | 559 if match: |
553 self.last_time = 0 | 560 if match.group(1) != self.progress_prefix: |
554 if (now - self.last_time) >= self.time_throttle: | 561 self.progress_prefix = match.group(1) |
555 self.last_time = now | 562 elif now - self.last_time < self.time_throttle: |
556 print line | 563 return |
| 564 self.last_time = now |
| 565 self.out_fh.write('[%s] ' % Elapsed()) |
| 566 print >> self.out_fh, line |
557 | 567 |
558 | 568 |
559 def FindGclientRoot(from_dir, filename='.gclient'): | 569 def FindGclientRoot(from_dir, filename='.gclient'): |
560 """Tries to find the gclient root.""" | 570 """Tries to find the gclient root.""" |
561 real_from_dir = os.path.realpath(from_dir) | 571 real_from_dir = os.path.realpath(from_dir) |
562 path = real_from_dir | 572 path = real_from_dir |
563 while not os.path.exists(os.path.join(path, filename)): | 573 while not os.path.exists(os.path.join(path, filename)): |
564 split_path = os.path.split(path) | 574 split_path = os.path.split(path) |
565 if not split_path[1]: | 575 if not split_path[1]: |
566 return None | 576 return None |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
676 class WorkItem(object): | 686 class WorkItem(object): |
677 """One work item.""" | 687 """One work item.""" |
678 # On cygwin, creating a lock throwing randomly when nearing ~100 locks. | 688 # On cygwin, creating a lock throwing randomly when nearing ~100 locks. |
679 # As a workaround, use a single lock. Yep you read it right. Single lock for | 689 # As a workaround, use a single lock. Yep you read it right. Single lock for |
680 # all the 100 objects. | 690 # all the 100 objects. |
681 lock = threading.Lock() | 691 lock = threading.Lock() |
682 | 692 |
683 def __init__(self, name): | 693 def __init__(self, name): |
684 # A unique string representing this work item. | 694 # A unique string representing this work item. |
685 self._name = name | 695 self._name = name |
| 696 self.outbuf = cStringIO.StringIO() |
| 697 self.start = self.finish = None |
686 | 698 |
687 def run(self, work_queue): | 699 def run(self, work_queue): |
688 """work_queue is passed as keyword argument so it should be | 700 """work_queue is passed as keyword argument so it should be |
689 the last parameters of the function when you override it.""" | 701 the last parameters of the function when you override it.""" |
690 pass | 702 pass |
691 | 703 |
692 @property | 704 @property |
693 def name(self): | 705 def name(self): |
694 return self._name | 706 return self._name |
695 | 707 |
696 | 708 |
697 class ExecutionQueue(object): | 709 class ExecutionQueue(object): |
698 """Runs a set of WorkItem that have interdependencies and were WorkItem are | 710 """Runs a set of WorkItem that have interdependencies and were WorkItem are |
699 added as they are processed. | 711 added as they are processed. |
700 | 712 |
701 In gclient's case, Dependencies sometime needs to be run out of order due to | 713 In gclient's case, Dependencies sometime needs to be run out of order due to |
702 From() keyword. This class manages that all the required dependencies are run | 714 From() keyword. This class manages that all the required dependencies are run |
703 before running each one. | 715 before running each one. |
704 | 716 |
705 Methods of this class are thread safe. | 717 Methods of this class are thread safe. |
706 """ | 718 """ |
707 def __init__(self, jobs, progress, ignore_requirements): | 719 def __init__(self, jobs, progress, ignore_requirements, verbose=False): |
708 """jobs specifies the number of concurrent tasks to allow. progress is a | 720 """jobs specifies the number of concurrent tasks to allow. progress is a |
709 Progress instance.""" | 721 Progress instance.""" |
710 # Set when a thread is done or a new item is enqueued. | 722 # Set when a thread is done or a new item is enqueued. |
711 self.ready_cond = threading.Condition() | 723 self.ready_cond = threading.Condition() |
712 # Maximum number of concurrent tasks. | 724 # Maximum number of concurrent tasks. |
713 self.jobs = jobs | 725 self.jobs = jobs |
714 # List of WorkItem, for gclient, these are Dependency instances. | 726 # List of WorkItem, for gclient, these are Dependency instances. |
715 self.queued = [] | 727 self.queued = [] |
716 # List of strings representing each Dependency.name that was run. | 728 # List of strings representing each Dependency.name that was run. |
717 self.ran = [] | 729 self.ran = [] |
718 # List of items currently running. | 730 # List of items currently running. |
719 self.running = [] | 731 self.running = [] |
720 # Exceptions thrown if any. | 732 # Exceptions thrown if any. |
721 self.exceptions = Queue.Queue() | 733 self.exceptions = Queue.Queue() |
722 # Progress status | 734 # Progress status |
723 self.progress = progress | 735 self.progress = progress |
724 if self.progress: | 736 if self.progress: |
725 self.progress.update(0) | 737 self.progress.update(0) |
726 | 738 |
727 self.ignore_requirements = ignore_requirements | 739 self.ignore_requirements = ignore_requirements |
| 740 self.verbose = verbose |
| 741 self.last_join = None |
| 742 self.last_subproc_output = None |
728 | 743 |
729 def enqueue(self, d): | 744 def enqueue(self, d): |
730 """Enqueue one Dependency to be executed later once its requirements are | 745 """Enqueue one Dependency to be executed later once its requirements are |
731 satisfied. | 746 satisfied. |
732 """ | 747 """ |
733 assert isinstance(d, WorkItem) | 748 assert isinstance(d, WorkItem) |
734 self.ready_cond.acquire() | 749 self.ready_cond.acquire() |
735 try: | 750 try: |
736 self.queued.append(d) | 751 self.queued.append(d) |
737 total = len(self.queued) + len(self.ran) + len(self.running) | 752 total = len(self.queued) + len(self.ran) + len(self.running) |
738 logging.debug('enqueued(%s)' % d.name) | 753 logging.debug('enqueued(%s)' % d.name) |
739 if self.progress: | 754 if self.progress: |
740 self.progress._total = total + 1 | 755 self.progress._total = total + 1 |
741 self.progress.update(0) | 756 self.progress.update(0) |
742 self.ready_cond.notifyAll() | 757 self.ready_cond.notifyAll() |
743 finally: | 758 finally: |
744 self.ready_cond.release() | 759 self.ready_cond.release() |
745 | 760 |
| 761 def out_cb(self, _): |
| 762 self.last_subproc_output = datetime.datetime.now() |
| 763 return True |
| 764 |
| 765 @staticmethod |
| 766 def format_task_output(task, comment=''): |
| 767 if comment: |
| 768 comment = ' (%s)' % comment |
| 769 if task.start and task.finish: |
| 770 elapsed = ' (Elapsed: %s)' % ( |
| 771 str(task.finish - task.start).partition('.')[0]) |
| 772 else: |
| 773 elapsed = '' |
| 774 return """ |
| 775 %s%s%s |
| 776 ---------------------------------------- |
| 777 %s |
| 778 ----------------------------------------""" % ( |
| 779 task.name, comment, task.outbuf.getvalue().strip(), elapsed) |
| 780 |
746 def flush(self, *args, **kwargs): | 781 def flush(self, *args, **kwargs): |
747 """Runs all enqueued items until all are executed.""" | 782 """Runs all enqueued items until all are executed.""" |
748 kwargs['work_queue'] = self | 783 kwargs['work_queue'] = self |
| 784 self.last_subproc_output = self.last_join = datetime.datetime.now() |
749 self.ready_cond.acquire() | 785 self.ready_cond.acquire() |
750 try: | 786 try: |
751 while True: | 787 while True: |
752 # Check for task to run first, then wait. | 788 # Check for task to run first, then wait. |
753 while True: | 789 while True: |
754 if not self.exceptions.empty(): | 790 if not self.exceptions.empty(): |
755 # Systematically flush the queue when an exception logged. | 791 # Systematically flush the queue when an exception logged. |
756 self.queued = [] | 792 self.queued = [] |
757 self._flush_terminated_threads() | 793 self._flush_terminated_threads() |
758 if (not self.queued and not self.running or | 794 if (not self.queued and not self.running or |
(...skipping 12 matching lines...) Expand all Loading... |
771 else: | 807 else: |
772 # Couldn't find an item that could run. Break out the outher loop. | 808 # Couldn't find an item that could run. Break out the outher loop. |
773 break | 809 break |
774 | 810 |
775 if not self.queued and not self.running: | 811 if not self.queued and not self.running: |
776 # We're done. | 812 # We're done. |
777 break | 813 break |
778 # We need to poll here otherwise Ctrl-C isn't processed. | 814 # We need to poll here otherwise Ctrl-C isn't processed. |
779 try: | 815 try: |
780 self.ready_cond.wait(10) | 816 self.ready_cond.wait(10) |
| 817 # If we haven't printed to terminal for a while, but we have received |
| 818 # spew from a suprocess, let the user know we're still progressing. |
| 819 now = datetime.datetime.now() |
| 820 if (now - self.last_join > datetime.timedelta(seconds=60) and |
| 821 self.last_subproc_output > self.last_join): |
| 822 if self.progress: |
| 823 print >> sys.stdout, '' |
| 824 elapsed = Elapsed() |
| 825 print >> sys.stdout, '[%s] Still working on:' % elapsed |
| 826 for task in self.running: |
| 827 print >> sys.stdout, '[%s] %s' % (elapsed, task.item.name) |
781 except KeyboardInterrupt: | 828 except KeyboardInterrupt: |
782 # Help debugging by printing some information: | 829 # Help debugging by printing some information: |
783 print >> sys.stderr, ( | 830 print >> sys.stderr, ( |
784 ('\nAllowed parallel jobs: %d\n# queued: %d\nRan: %s\n' | 831 ('\nAllowed parallel jobs: %d\n# queued: %d\nRan: %s\n' |
785 'Running: %d') % ( | 832 'Running: %d') % ( |
786 self.jobs, | 833 self.jobs, |
787 len(self.queued), | 834 len(self.queued), |
788 ', '.join(self.ran), | 835 ', '.join(self.ran), |
789 len(self.running))) | 836 len(self.running))) |
790 for i in self.queued: | 837 for i in self.queued: |
791 print >> sys.stderr, '%s: %s' % (i.name, ', '.join(i.requirements)) | 838 print >> sys.stderr, '%s (not started): %s' % ( |
| 839 i.name, ', '.join(i.requirements)) |
| 840 for i in self.running: |
| 841 print >> sys.stderr, self.format_task_output(i.item, 'interrupted') |
792 raise | 842 raise |
793 # Something happened: self.enqueue() or a thread terminated. Loop again. | 843 # Something happened: self.enqueue() or a thread terminated. Loop again. |
794 finally: | 844 finally: |
795 self.ready_cond.release() | 845 self.ready_cond.release() |
796 | 846 |
797 assert not self.running, 'Now guaranteed to be single-threaded' | 847 assert not self.running, 'Now guaranteed to be single-threaded' |
798 if not self.exceptions.empty(): | 848 if not self.exceptions.empty(): |
| 849 if self.progress: |
| 850 print >> sys.stdout, '' |
799 # To get back the stack location correctly, the raise a, b, c form must be | 851 # To get back the stack location correctly, the raise a, b, c form must be |
800 # used, passing a tuple as the first argument doesn't work. | 852 # used, passing a tuple as the first argument doesn't work. |
801 e = self.exceptions.get() | 853 e, task = self.exceptions.get() |
| 854 print >> sys.stderr, self.format_task_output(task.item, 'ERROR') |
802 raise e[0], e[1], e[2] | 855 raise e[0], e[1], e[2] |
803 if self.progress: | 856 elif self.progress: |
804 self.progress.end() | 857 self.progress.end() |
805 | 858 |
806 def _flush_terminated_threads(self): | 859 def _flush_terminated_threads(self): |
807 """Flush threads that have terminated.""" | 860 """Flush threads that have terminated.""" |
808 running = self.running | 861 running = self.running |
809 self.running = [] | 862 self.running = [] |
810 for t in running: | 863 for t in running: |
811 if t.isAlive(): | 864 if t.isAlive(): |
812 self.running.append(t) | 865 self.running.append(t) |
813 else: | 866 else: |
814 t.join() | 867 t.join() |
| 868 self.last_join = datetime.datetime.now() |
815 sys.stdout.flush() | 869 sys.stdout.flush() |
| 870 if self.verbose: |
| 871 print >> sys.stdout, self.format_task_output(t.item) |
816 if self.progress: | 872 if self.progress: |
817 self.progress.update(1, t.item.name) | 873 self.progress.update(1, t.item.name) |
818 if t.item.name in self.ran: | 874 if t.item.name in self.ran: |
819 raise Error( | 875 raise Error( |
820 'gclient is confused, "%s" is already in "%s"' % ( | 876 'gclient is confused, "%s" is already in "%s"' % ( |
821 t.item.name, ', '.join(self.ran))) | 877 t.item.name, ', '.join(self.ran))) |
822 if not t.item.name in self.ran: | 878 if not t.item.name in self.ran: |
823 self.ran.append(t.item.name) | 879 self.ran.append(t.item.name) |
824 | 880 |
825 def _run_one_task(self, task_item, args, kwargs): | 881 def _run_one_task(self, task_item, args, kwargs): |
826 if self.jobs > 1: | 882 if self.jobs > 1: |
827 # Start the thread. | 883 # Start the thread. |
828 index = len(self.ran) + len(self.running) + 1 | 884 index = len(self.ran) + len(self.running) + 1 |
829 new_thread = self._Worker(task_item, index, args, kwargs) | 885 new_thread = self._Worker(task_item, index, args, kwargs) |
830 self.running.append(new_thread) | 886 self.running.append(new_thread) |
831 new_thread.start() | 887 new_thread.start() |
832 else: | 888 else: |
833 # Run the 'thread' inside the main thread. Don't try to catch any | 889 # Run the 'thread' inside the main thread. Don't try to catch any |
834 # exception. | 890 # exception. |
835 task_item.run(*args, **kwargs) | 891 try: |
836 self.ran.append(task_item.name) | 892 task_item.start = datetime.datetime.now() |
837 if self.progress: | 893 print >> task_item.outbuf, '[%s] Started.' % Elapsed(task_item.start) |
838 self.progress.update(1, ', '.join(t.item.name for t in self.running)) | 894 task_item.run(*args, **kwargs) |
| 895 task_item.finish = datetime.datetime.now() |
| 896 print >> task_item.outbuf, '[%s] Finished.' % Elapsed(task_item.finish) |
| 897 self.ran.append(task_item.name) |
| 898 if self.verbose: |
| 899 if self.progress: |
| 900 print >> sys.stdout, '' |
| 901 print >> sys.stdout, self.format_task_output(task_item) |
| 902 if self.progress: |
| 903 self.progress.update(1, ', '.join(t.item.name for t in self.running)) |
| 904 except KeyboardInterrupt: |
| 905 print >> sys.stderr, self.format_task_output(task_item, 'interrupted') |
| 906 raise |
| 907 except Exception: |
| 908 print >> sys.stderr, self.format_task_output(task_item, 'ERROR') |
| 909 raise |
| 910 |
839 | 911 |
840 class _Worker(threading.Thread): | 912 class _Worker(threading.Thread): |
841 """One thread to execute one WorkItem.""" | 913 """One thread to execute one WorkItem.""" |
842 def __init__(self, item, index, args, kwargs): | 914 def __init__(self, item, index, args, kwargs): |
843 threading.Thread.__init__(self, name=item.name or 'Worker') | 915 threading.Thread.__init__(self, name=item.name or 'Worker') |
844 logging.info('_Worker(%s) reqs:%s' % (item.name, item.requirements)) | 916 logging.info('_Worker(%s) reqs:%s' % (item.name, item.requirements)) |
845 self.item = item | 917 self.item = item |
846 self.index = index | 918 self.index = index |
847 self.args = args | 919 self.args = args |
848 self.kwargs = kwargs | 920 self.kwargs = kwargs |
849 self.daemon = True | 921 self.daemon = True |
850 | 922 |
851 def run(self): | 923 def run(self): |
852 """Runs in its own thread.""" | 924 """Runs in its own thread.""" |
853 logging.debug('_Worker.run(%s)' % self.item.name) | 925 logging.debug('_Worker.run(%s)' % self.item.name) |
854 work_queue = self.kwargs['work_queue'] | 926 work_queue = self.kwargs['work_queue'] |
855 try: | 927 try: |
| 928 self.item.start = datetime.datetime.now() |
| 929 print >> self.item.outbuf, '[%s] Started.' % Elapsed(self.item.start) |
856 self.item.run(*self.args, **self.kwargs) | 930 self.item.run(*self.args, **self.kwargs) |
| 931 self.item.finish = datetime.datetime.now() |
| 932 print >> self.item.outbuf, '[%s] Finished.' % Elapsed(self.item.finish) |
857 except KeyboardInterrupt: | 933 except KeyboardInterrupt: |
858 logging.info('Caught KeyboardInterrupt in thread %s', self.item.name) | 934 logging.info('Caught KeyboardInterrupt in thread %s', self.item.name) |
859 logging.info(str(sys.exc_info())) | 935 logging.info(str(sys.exc_info())) |
860 work_queue.exceptions.put(sys.exc_info()) | 936 work_queue.exceptions.put((sys.exc_info(), self)) |
861 raise | 937 raise |
862 except Exception: | 938 except Exception: |
863 # Catch exception location. | 939 # Catch exception location. |
864 logging.info('Caught exception in thread %s', self.item.name) | 940 logging.info('Caught exception in thread %s', self.item.name) |
865 logging.info(str(sys.exc_info())) | 941 logging.info(str(sys.exc_info())) |
866 work_queue.exceptions.put(sys.exc_info()) | 942 work_queue.exceptions.put((sys.exc_info(), self)) |
867 finally: | 943 finally: |
868 logging.info('_Worker.run(%s) done', self.item.name) | 944 logging.info('_Worker.run(%s) done', self.item.name) |
869 work_queue.ready_cond.acquire() | 945 work_queue.ready_cond.acquire() |
870 try: | 946 try: |
871 work_queue.ready_cond.notifyAll() | 947 work_queue.ready_cond.notifyAll() |
872 finally: | 948 finally: |
873 work_queue.ready_cond.release() | 949 work_queue.ready_cond.release() |
874 | 950 |
875 | 951 |
876 def GetEditor(git, git_editor=None): | 952 def GetEditor(git, git_editor=None): |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1001 def DefaultIndexPackConfig(url=''): | 1077 def DefaultIndexPackConfig(url=''): |
1002 """Return reasonable default values for configuring git-index-pack. | 1078 """Return reasonable default values for configuring git-index-pack. |
1003 | 1079 |
1004 Experiments suggest that higher values for pack.threads don't improve | 1080 Experiments suggest that higher values for pack.threads don't improve |
1005 performance.""" | 1081 performance.""" |
1006 cache_limit = DefaultDeltaBaseCacheLimit() | 1082 cache_limit = DefaultDeltaBaseCacheLimit() |
1007 result = ['-c', 'core.deltaBaseCacheLimit=%s' % cache_limit] | 1083 result = ['-c', 'core.deltaBaseCacheLimit=%s' % cache_limit] |
1008 if url in THREADED_INDEX_PACK_BLACKLIST: | 1084 if url in THREADED_INDEX_PACK_BLACKLIST: |
1009 result.extend(['-c', 'pack.threads=1']) | 1085 result.extend(['-c', 'pack.threads=1']) |
1010 return result | 1086 return result |
OLD | NEW |