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

Side by Side Diff: parallel_emerge

Issue 3534006: Cleanup parallel_emerge exit conditions to fix hangs. (Closed) Base URL: http://git.chromium.org/git/crosutils.git
Patch Set: More comments. Created 10 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python2.6 1 #!/usr/bin/python2.6
2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Program to run emerge in parallel, for significant speedup. 6 """Program to run emerge in parallel, for significant speedup.
7 7
8 Usage: 8 Usage:
9 ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps] 9 ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps]
10 [emerge args] package" 10 [emerge args] package"
(...skipping 22 matching lines...) Expand all
33 * Some ebuild packages have incorrectly specified deps, and running 33 * Some ebuild packages have incorrectly specified deps, and running
34 them in parallel is more likely to bring out these failures. 34 them in parallel is more likely to bring out these failures.
35 * Some ebuilds (especially the build part) have complex dependencies 35 * Some ebuilds (especially the build part) have complex dependencies
36 that are not captured well by this script (it may be necessary to 36 that are not captured well by this script (it may be necessary to
37 install an old package to build, but then install a newer version 37 install an old package to build, but then install a newer version
38 of the same package for a runtime dep). 38 of the same package for a runtime dep).
39 """ 39 """
40 40
41 import codecs 41 import codecs
42 import copy 42 import copy
43 import errno
43 import multiprocessing 44 import multiprocessing
44 import os 45 import os
45 import Queue 46 import Queue
46 import shlex 47 import shlex
47 import signal 48 import signal
48 import sys 49 import sys
49 import tempfile 50 import tempfile
50 import time 51 import time
51 import traceback 52 import traceback
52 import urllib2 53 import urllib2
(...skipping 1248 matching lines...) Expand 10 before | Expand all | Expand 10 after
1301 """ 1302 """
1302 1303
1303 SetupWorkerSignals() 1304 SetupWorkerSignals()
1304 settings, trees, mtimedb = emerge.settings, emerge.trees, emerge.mtimedb 1305 settings, trees, mtimedb = emerge.settings, emerge.trees, emerge.mtimedb
1305 opts, spinner = emerge.opts, emerge.spinner 1306 opts, spinner = emerge.opts, emerge.spinner
1306 opts["--nodeps"] = True 1307 opts["--nodeps"] = True
1307 while True: 1308 while True:
1308 # Wait for a new item to show up on the queue. This is a blocking wait, 1309 # Wait for a new item to show up on the queue. This is a blocking wait,
1309 # so if there's nothing to do, we just sit here. 1310 # so if there's nothing to do, we just sit here.
1310 target = task_queue.get() 1311 target = task_queue.get()
1312 if not target:
1313 # If target is None, this means that the main thread wants us to quit.
1314 # The other workers need to exit too, so we'll push the message back on
1315 # to the queue so they'll get it too.
1316 task_queue.put(target)
1317 return
1311 db_pkg = package_db[target] 1318 db_pkg = package_db[target]
1312 db_pkg.root_config = emerge.root_config 1319 db_pkg.root_config = emerge.root_config
1313 install_list = [db_pkg] 1320 install_list = [db_pkg]
1314 pkgname = db_pkg.pf 1321 pkgname = db_pkg.pf
1315 output = tempfile.NamedTemporaryFile(prefix=pkgname + "-", delete=False) 1322 output = tempfile.NamedTemporaryFile(prefix=pkgname + "-", delete=False)
1316 start_timestamp = time.time() 1323 start_timestamp = time.time()
1317 job = EmergeJobState(target, pkgname, False, output.name, start_timestamp) 1324 job = EmergeJobState(target, pkgname, False, output.name, start_timestamp)
1318 job_queue.put(job) 1325 job_queue.put(job)
1319 if "--pretend" in opts: 1326 if "--pretend" in opts:
1320 retcode = 0 1327 retcode = 0
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1405 print "=== Complete: %s ===" % info 1412 print "=== Complete: %s ===" % info
1406 else: 1413 else:
1407 print "=== Still running: %s ===" % info 1414 print "=== Still running: %s ===" % info
1408 1415
1409 if self.unlink: 1416 if self.unlink:
1410 os.unlink(job.filename) 1417 os.unlink(job.filename)
1411 1418
1412 1419
1413 def PrintWorker(queue): 1420 def PrintWorker(queue):
1414 """A worker that prints stuff to the screen as requested.""" 1421 """A worker that prints stuff to the screen as requested."""
1415 SetupWorkerSignals() 1422
1423 def ExitHandler(signum, frame):
1424 # Switch to default signal handlers so that we'll die after two signals.
1425 signal.signal(signal.SIGINT, signal.SIG_DFL)
1426 signal.signal(signal.SIGTERM, signal.SIG_DFL)
1427
1428 # Don't exit on the first SIGINT / SIGTERM, because the parent worker will
1429 # handle it and tell us when we need to exit.
1430 signal.signal(signal.SIGINT, ExitHandler)
1431 signal.signal(signal.SIGTERM, ExitHandler)
1432
1433 # seek_locations is a map indicating the position we are at in each file.
1434 # It starts off empty, but is set by the various Print jobs as we go along
1435 # to indicate where we left off in each file.
1416 seek_locations = {} 1436 seek_locations = {}
1417 while True: 1437 while True:
1418 job = queue.get() 1438 try:
1419 if job: 1439 job = queue.get()
1420 job.Print(seek_locations) 1440 if job:
1421 else: 1441 job.Print(seek_locations)
1422 break 1442 else:
1443 break
1444 except IOError as ex:
1445 if ex.errno == errno.EINTR:
1446 # Looks like we received a signal. Keep printing.
1447 continue
1448 raise
1423 1449
1424 1450
1425 class EmergeQueue(object): 1451 class EmergeQueue(object):
1426 """Class to schedule emerge jobs according to a dependency graph.""" 1452 """Class to schedule emerge jobs according to a dependency graph."""
1427 1453
1428 def __init__(self, deps_map, emerge, package_db, show_output): 1454 def __init__(self, deps_map, emerge, package_db, show_output):
1429 # Store the dependency graph. 1455 # Store the dependency graph.
1430 self._deps_map = deps_map 1456 self._deps_map = deps_map
1431 # Initialize the running queue to empty 1457 # Initialize the running queue to empty
1432 self._jobs = {} 1458 self._jobs = {}
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
1483 signal.signal(signal.SIGTERM, signal.SIG_DFL) 1509 signal.signal(signal.SIGTERM, signal.SIG_DFL)
1484 1510
1485 # Print our current job status 1511 # Print our current job status
1486 for target, job in self._jobs.iteritems(): 1512 for target, job in self._jobs.iteritems():
1487 if job: 1513 if job:
1488 self._print_queue.put(JobPrinter(job, unlink=True)) 1514 self._print_queue.put(JobPrinter(job, unlink=True))
1489 1515
1490 # Notify the user that we are exiting 1516 # Notify the user that we are exiting
1491 self._Print("Exiting on signal %s" % signum) 1517 self._Print("Exiting on signal %s" % signum)
1492 1518
1493 # Exit when print worker is done. 1519 # Kill child threads, then exit.
1494 self._print_queue.put(None) 1520 self._Exit()
1495 self._print_worker.join()
1496 sys.exit(1) 1521 sys.exit(1)
1497 1522
1498 # Print out job status when we are killed 1523 # Print out job status when we are killed
1499 signal.signal(signal.SIGINT, ExitHandler) 1524 signal.signal(signal.SIGINT, ExitHandler)
1500 signal.signal(signal.SIGTERM, ExitHandler) 1525 signal.signal(signal.SIGTERM, ExitHandler)
1501 1526
1502 def _Schedule(self, target): 1527 def _Schedule(self, target):
1503 # We maintain a tree of all deps, if this doesn't need 1528 # We maintain a tree of all deps, if this doesn't need
1504 # to be installed just free up it's children and continue. 1529 # to be installed just free up it's children and continue.
1505 # It is possible to reinstall deps of deps, without reinstalling 1530 # It is possible to reinstall deps of deps, without reinstalling
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
1551 if not self._deps_map[dep]["needs"]: 1576 if not self._deps_map[dep]["needs"]:
1552 self._Schedule(dep) 1577 self._Schedule(dep)
1553 self._deps_map.pop(target) 1578 self._deps_map.pop(target)
1554 1579
1555 def _Retry(self): 1580 def _Retry(self):
1556 if self._retry_queue: 1581 if self._retry_queue:
1557 target = self._retry_queue.pop(0) 1582 target = self._retry_queue.pop(0)
1558 self._Schedule(target) 1583 self._Schedule(target)
1559 self._Print("Retrying emerge of %s." % target) 1584 self._Print("Retrying emerge of %s." % target)
1560 1585
1586 def _Exit(self):
1587 # Tell emerge workers to exit. They all exit when 'None' is pushed
1588 # to the queue.
1589 self._emerge_queue.put(None)
1590 self._pool.close()
1591 self._pool.join()
1592
1593 # Now that our workers are finished, we can kill the print queue.
1594 self._print_queue.put(None)
1595 self._print_worker.join()
1596
1561 def Run(self): 1597 def Run(self):
1562 """Run through the scheduled ebuilds. 1598 """Run through the scheduled ebuilds.
1563 1599
1564 Keep running so long as we have uninstalled packages in the 1600 Keep running so long as we have uninstalled packages in the
1565 dependency graph to merge. 1601 dependency graph to merge.
1566 """ 1602 """
1567 while self._deps_map: 1603 while self._deps_map:
1568 # Check here that we are actually waiting for something. 1604 # Check here that we are actually waiting for something.
1569 if (self._emerge_queue.empty() and 1605 if (self._emerge_queue.empty() and
1570 self._job_queue.empty() and 1606 self._job_queue.empty() and
1571 not self._jobs and 1607 not self._jobs and
1572 self._deps_map): 1608 self._deps_map):
1573 # If we have failed on a package, retry it now. 1609 # If we have failed on a package, retry it now.
1574 if self._retry_queue: 1610 if self._retry_queue:
1575 self._Retry() 1611 self._Retry()
1576 else: 1612 else:
1577 # Tell the print worker we're done, and wait for it to exit. 1613 # Tell child threads to exit.
1578 self._print_queue.put(None) 1614 self._Exit()
1579 self._print_worker.join()
1580 1615
1581 # The dependency map is helpful for debugging failures. 1616 # The dependency map is helpful for debugging failures.
1582 PrintDepsMap(self._deps_map) 1617 PrintDepsMap(self._deps_map)
1583 1618
1584 # Tell the user why we're exiting. 1619 # Tell the user why we're exiting.
1585 if self._failed: 1620 if self._failed:
1586 print "Packages failed: %s" % " ,".join(self._failed) 1621 print "Packages failed: %s" % " ,".join(self._failed)
1587 else: 1622 else:
1588 print "Deadlock! Circular dependencies!" 1623 print "Deadlock! Circular dependencies!"
1589 sys.exit(1) 1624 sys.exit(1)
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1630 # one retrying package actively running at a time. 1665 # one retrying package actively running at a time.
1631 self._Retry() 1666 self._Retry()
1632 1667
1633 self._Print("Completed %s" % details) 1668 self._Print("Completed %s" % details)
1634 # Mark as completed and unblock waiting ebuilds. 1669 # Mark as completed and unblock waiting ebuilds.
1635 self._Finish(target) 1670 self._Finish(target)
1636 1671
1637 # Print an update. 1672 # Print an update.
1638 self._Status() 1673 self._Status()
1639 1674
1640 # Tell the print worker we're done, and wait for it to exit. 1675 # Tell child threads to exit.
1641 self._print_queue.put(None) 1676 self._Print("Merge complete")
1642 self._print_worker.join() 1677 self._Exit()
1643 1678
1644 1679
1645 def main(): 1680 def main():
1646 1681
1647 deps = DepGraphGenerator() 1682 deps = DepGraphGenerator()
1648 deps.Initialize(sys.argv[1:]) 1683 deps.Initialize(sys.argv[1:])
1649 emerge = deps.emerge 1684 emerge = deps.emerge
1650 1685
1651 if emerge.action is not None: 1686 if emerge.action is not None:
1652 sys.argv = deps.ParseParallelEmergeArgs(sys.argv) 1687 sys.argv = deps.ParseParallelEmergeArgs(sys.argv)
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
1730 # need to upgrade the rest of the packages. So we'll go ahead and do that. 1765 # need to upgrade the rest of the packages. So we'll go ahead and do that.
1731 if portage_upgrade: 1766 if portage_upgrade:
1732 args = sys.argv[1:] + ["--nomerge=sys-apps/portage"] 1767 args = sys.argv[1:] + ["--nomerge=sys-apps/portage"]
1733 os.execvp(os.path.realpath(sys.argv[0]), args) 1768 os.execvp(os.path.realpath(sys.argv[0]), args)
1734 1769
1735 print "Done" 1770 print "Done"
1736 sys.exit(0) 1771 sys.exit(0)
1737 1772
1738 if __name__ == "__main__": 1773 if __name__ == "__main__":
1739 main() 1774 main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698