OLD | NEW |
1 # Copyright 2009 Google Inc. All Rights Reserved. | 1 # Copyright 2009 Google Inc. All Rights Reserved. |
2 # | 2 # |
3 # Licensed under the Apache License, Version 2.0 (the "License"); | 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
4 # you may not use this file except in compliance with the License. | 4 # you may not use this file except in compliance with the License. |
5 # You may obtain a copy of the License at | 5 # You may obtain a copy of the License at |
6 # | 6 # |
7 # http://www.apache.org/licenses/LICENSE-2.0 | 7 # http://www.apache.org/licenses/LICENSE-2.0 |
8 # | 8 # |
9 # Unless required by applicable law or agreed to in writing, software | 9 # Unless required by applicable law or agreed to in writing, software |
10 # distributed under the License is distributed on an "AS IS" BASIS, | 10 # distributed under the License is distributed on an "AS IS" BASIS, |
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 # See the License for the specific language governing permissions and | 12 # See the License for the specific language governing permissions and |
13 # limitations under the License. | 13 # limitations under the License. |
14 | 14 |
15 """Generic utils.""" | 15 """Generic utils.""" |
16 | 16 |
17 import errno | 17 import errno |
18 import logging | 18 import logging |
19 import os | 19 import os |
| 20 from third_party.repo.progress import Progress |
| 21 import Queue |
20 import re | 22 import re |
21 import stat | 23 import stat |
22 import subprocess | 24 import subprocess |
23 import sys | 25 import sys |
24 import time | 26 import time |
| 27 import threading |
| 28 import traceback |
25 import xml.dom.minidom | 29 import xml.dom.minidom |
26 import xml.parsers.expat | 30 import xml.parsers.expat |
27 | 31 |
28 | 32 |
29 class CheckCallError(OSError): | 33 class CheckCallError(OSError): |
30 """CheckCall() returned non-0.""" | 34 """CheckCall() returned non-0.""" |
31 def __init__(self, command, cwd, retcode, stdout, stderr=None): | 35 def __init__(self, command, cwd, retcode, stdout, stderr=None): |
32 OSError.__init__(self, command, cwd, retcode, stdout, stderr) | 36 OSError.__init__(self, command, cwd, retcode, stdout, stderr) |
33 self.command = command | 37 self.command = command |
34 self.cwd = cwd | 38 self.cwd = cwd |
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
364 config_path = FindFileUpwards(config_file, path) | 368 config_path = FindFileUpwards(config_file, path) |
365 | 369 |
366 if not config_path: | 370 if not config_path: |
367 print "Can't find %s" % config_file | 371 print "Can't find %s" % config_file |
368 return None | 372 return None |
369 | 373 |
370 env = {} | 374 env = {} |
371 execfile(config_path, env) | 375 execfile(config_path, env) |
372 config_dir = os.path.dirname(config_path) | 376 config_dir = os.path.dirname(config_path) |
373 return config_dir, env['entries'] | 377 return config_dir, env['entries'] |
| 378 |
| 379 |
| 380 class ThreadPool: |
| 381 """A thread pool class that lets one schedule jobs on many worker threads.""" |
| 382 |
| 383 def __init__(self, threads=1): |
| 384 self._threads = threads |
| 385 self._queue = Queue.Queue() |
| 386 self._jobs_left = 0 |
| 387 self._condition = threading.Condition() |
| 388 self._workers = [] |
| 389 |
| 390 class Worker(threading.Thread): |
| 391 """Internal worker class that executes jobs from the ThreadPool queue.""" |
| 392 |
| 393 def __init__(self, pool): |
| 394 threading.Thread.__init__(self) |
| 395 self.setDaemon(True) |
| 396 self._pool = pool |
| 397 self._done = False |
| 398 self.exceptions = [] |
| 399 |
| 400 def Done(self): |
| 401 """Terminates the worker threads.""" |
| 402 self._done = True |
| 403 |
| 404 def run(self): |
| 405 """Executes jobs from the pool's queue.""" |
| 406 while not self._done: |
| 407 f = self._pool._queue.get() |
| 408 try: |
| 409 try: |
| 410 f(self) |
| 411 except Exception, e: |
| 412 # Catch all exceptions, otherwise we can't join the thread. Print |
| 413 # the backtrace now, but keep the exception so that we can raise it |
| 414 # on the main thread. |
| 415 type, value, tb = sys.exc_info() |
| 416 traceback.print_exception(type, value, tb) |
| 417 self.exceptions.append(e) |
| 418 finally: |
| 419 self._pool._JobDone() |
| 420 |
| 421 def _AddJobToQueue(self, job): |
| 422 self._condition.acquire() |
| 423 self._queue.put(job) |
| 424 self._jobs_left += 1 |
| 425 self._condition.release() |
| 426 |
| 427 def _JobDone(self): |
| 428 self._condition.acquire() |
| 429 try: |
| 430 assert self._jobs_left |
| 431 self._jobs_left -= 1 |
| 432 if self._jobs_left == 0: |
| 433 self._condition.notify() |
| 434 finally: |
| 435 self._condition.release() |
| 436 |
| 437 def _JoinQueue(self): |
| 438 self._condition.acquire() |
| 439 try: |
| 440 while self._jobs_left: |
| 441 self._condition.wait(1) |
| 442 finally: |
| 443 self._condition.release() |
| 444 |
| 445 def Start(self): |
| 446 """Starts the thread pool. Spawns worker threads.""" |
| 447 if not self._threads: |
| 448 return |
| 449 assert not self._workers |
| 450 for i in xrange(0, self._threads): |
| 451 worker = self.Worker(self) |
| 452 self._workers.append(worker) |
| 453 worker.start() |
| 454 |
| 455 def Stop(self): |
| 456 """Stops the thread pool. Joins all worker threads.""" |
| 457 if not self._threads: |
| 458 return |
| 459 assert self._workers |
| 460 for i in xrange(0, len(self._workers)): |
| 461 wrapped = lambda thread: thread.Done() |
| 462 self._AddJobToQueue(wrapped) |
| 463 self._JoinQueue() |
| 464 for worker in self._workers: |
| 465 while True: |
| 466 worker.join(1) |
| 467 if not worker.isAlive(): |
| 468 break |
| 469 try: |
| 470 for worker in self._workers: |
| 471 for e in worker.exceptions: |
| 472 # If we collected exceptions, raise them now. |
| 473 raise e |
| 474 finally: |
| 475 self._workers = [] |
| 476 |
| 477 def AddJob(self, function): |
| 478 """Adds a job to the queue. |
| 479 |
| 480 A job is a simple closure, that will get executed on one of the worker |
| 481 threads.""" |
| 482 if self._threads: |
| 483 wrapped = lambda worker: function() |
| 484 self._AddJobToQueue(wrapped) |
| 485 else: |
| 486 function() |
| 487 |
| 488 def WaitJobs(self): |
| 489 """Waits for all jobs to be completed.""" |
| 490 if not self._threads: |
| 491 return |
| 492 assert self._workers |
| 493 self._JoinQueue() |
| 494 |
| 495 |
| 496 class ThreadSafeProgress(Progress): |
| 497 """A thread safe progress meter. |
| 498 |
| 499 This class just wraps around a Progress class, that just forwards calls while |
| 500 holding a lock.""" |
| 501 def __init__(self, title, total=0): |
| 502 Progress.__init__(self, title, total) |
| 503 self._lock = threading.Lock() |
| 504 |
| 505 def update(self, inc=1): |
| 506 self._lock.acquire() |
| 507 Progress.update(self, inc) |
| 508 self._lock.release() |
| 509 |
| 510 def end(self): |
| 511 self._lock.acquire() |
| 512 Progress.end(self) |
| 513 self._lock.release() |
OLD | NEW |