From 89d51c245c9ecf95c2439e4cfe0d36c123d87bb0 Mon Sep 17 00:00:00 2001 From: "maruel@chromium.org" Date: Wed, 14 Sep 2011 20:12:52 +0000 Subject: [PATCH] Initial step into making Dependency thread safe R=dpranke@chromium.org BUG= TEST= Review URL: http://codereview.chromium.org/7892034 git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@101135 0039d316-1c4b-4281-b951-d872f2087c98 --- gclient.py | 13 +++++-------- gclient_utils.py | 32 +++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/gclient.py b/gclient.py index f568662ba..c836d9ec3 100644 --- a/gclient.py +++ b/gclient.py @@ -145,9 +145,8 @@ class Dependency(GClientKeywords, gclient_utils.WorkItem): # self.dependencies and self.requirements are read and modified from # multiple threads at the same time. Sad. GClientKeywords.__init__(self) - gclient_utils.WorkItem.__init__(self) + gclient_utils.WorkItem.__init__(self, name) self.parent = parent - self.name = name self.url = url self.parsed_url = None # These 2 are only set in .gclient and not in DEPS files. @@ -169,8 +168,6 @@ class Dependency(GClientKeywords, gclient_utils.WorkItem): self.processed = False # This dependency had its hook run self.hooks_ran = False - # Required dependencies to run before running this one: - self.requirements = set() # Post process the url to remove trailing slashes. if isinstance(self.url, basestring): @@ -201,7 +198,7 @@ class Dependency(GClientKeywords, gclient_utils.WorkItem): # self.parent is implicitly a requirement. This will be recursive by # definition. if self.parent and self.parent.name: - self.requirements.add(self.parent.name) + self._requirements.add(self.parent.name) # For a tree with at least 2 levels*, the leaf node needs to depend # on the level higher up in an orderly way. @@ -219,10 +216,10 @@ class Dependency(GClientKeywords, gclient_utils.WorkItem): for i in range(0, root_deps.index(self.parent)): value = root_deps[i] if value.name: - self.requirements.add(value.name) + self._requirements.add(value.name) if isinstance(self.url, self.FromImpl): - self.requirements.add(self.url.module_name) + self._requirements.add(self.url.module_name) if self.name and self.should_process: def yield_full_tree(root): @@ -238,7 +235,7 @@ class Dependency(GClientKeywords, gclient_utils.WorkItem): continue # Step 1: Find any requirements self may need. if self.name.startswith(posixpath.join(obj.name, '')): - self.requirements.add(obj.name) + self._requirements.add(obj.name) # Step 2: Find any requirements self may impose. if obj.name.startswith(posixpath.join(self.name, '')): obj.requirements.add(self.name) diff --git a/gclient_utils.py b/gclient_utils.py index 89050b7ad..2402519a3 100644 --- a/gclient_utils.py +++ b/gclient_utils.py @@ -456,19 +456,45 @@ def GetGClientRootAndEntries(path=None): return config_dir, env['entries'] +def lockedmethod(method): + """Method decorator that holds self.lock for the duration of the call.""" + def inner(self, *args, **kwargs): + try: + try: + self.lock.acquire() + except KeyboardInterrupt: + print >> sys.stderr, 'Was deadlocked' + raise + return method(self, *args, **kwargs) + finally: + self.lock.release() + return inner + + class WorkItem(object): """One work item.""" - def __init__(self): + def __init__(self, name): # A list of string, each being a WorkItem name. - self.requirements = [] + self._requirements = set() # A unique string representing this work item. - self.name = None + self._name = name + self.lock = threading.RLock() + @lockedmethod def run(self, work_queue): """work_queue is passed as keyword argument so it should be the last parameters of the function when you override it.""" pass + @property + def name(self): + return self._name + + @property + @lockedmethod + def requirements(self): + return tuple(self._requirements) + class ExecutionQueue(object): """Runs a set of WorkItem that have interdependencies and were WorkItem are