diff options
author | James Ennis <james.ennis@codethink.co.uk> | 2019-07-09 12:47:06 +0100 |
---|---|---|
committer | bst-marge-bot <marge-bot@buildstream.build> | 2019-07-16 09:13:39 +0000 |
commit | 17091995171936d6e7bbdba8fe6ed8094226266d (patch) | |
tree | 3b97af082802382e7c2c0aaaa40a7485be7f53ea | |
parent | ff6984e3b07f5d70aa7250991f6ff700004ddcde (diff) | |
download | buildstream-17091995171936d6e7bbdba8fe6ed8094226266d.tar.gz |
element.py: Introduce __update_strict_cache_key_of_rdeps()
Once an Element's strict cache key is determined, we should
attempt to update the strict cache key of it's reverse
dependencies. The state of a reverse dependency will be
updated once all of its dependencies have strict cache keys
This patch introduces the potential for a RecursionError because
_update_state() can now trigger further _update_state calls (on
reverse dependencies). Therefore, the maximum recursion limit
for our "test_max_recursion_depth" test has been lowered.
If this becomes a problem, we can always consider setting a
larger recursion limit, for now, this change has been tested
with the Debian stack and works as expected.
-rw-r--r-- | src/buildstream/_pipeline.py | 5 | ||||
-rw-r--r-- | src/buildstream/element.py | 41 | ||||
-rw-r--r-- | tests/frontend/show.py | 2 |
3 files changed, 45 insertions, 3 deletions
diff --git a/src/buildstream/_pipeline.py b/src/buildstream/_pipeline.py index a96a3f731..a9d88befd 100644 --- a/src/buildstream/_pipeline.py +++ b/src/buildstream/_pipeline.py @@ -124,6 +124,11 @@ class Pipeline(): # def resolve_elements(self, targets): with self._context.messenger.timed_activity("Resolving cached state", silent_nested=True): + # XXX: Now that Element._update_state() can trigger recursive update_state calls + # it is possible that we could get a RecursionError. However, this is unlikely + # to happen, even for large projects (tested with the Debian stack). Although, + # if it does become a problem we may have to set the recursion limit to a + # greater value. for element in self.dependencies(targets, Scope.ALL): # Determine initial element state. element._update_state() diff --git a/src/buildstream/element.py b/src/buildstream/element.py index faf97a52e..25f18c351 100644 --- a/src/buildstream/element.py +++ b/src/buildstream/element.py @@ -209,8 +209,11 @@ class Element(Plugin): self.__build_dependencies = [] # Direct build dependency Elements self.__reverse_build_deps = set() # Direct reverse build dependency Elements self.__reverse_runtime_deps = set() # Direct reverse runtime dependency Elements + self.__build_deps_without_strict_cache_key = None # Number of build dependencies without a strict key + self.__runtime_deps_without_strict_cache_key = None # Number of runtime dependencies without a strict key self.__build_deps_uncached = None # Build dependencies which are not yet cached self.__runtime_deps_uncached = None # Runtime dependencies which are not yet cached + self.__updated_strict_cache_keys_of_rdeps = False # Whether we've updated strict cache keys of rdeps self.__ready_for_runtime = False # Whether the element has all dependencies ready and has a cache key self.__ready_for_runtime_and_cached = False # Whether all runtime deps are cached, as well as the element self.__sources = [] # List of Sources @@ -962,13 +965,17 @@ class Element(Plugin): dependency = Element._new_from_meta(meta_dep) element.__runtime_dependencies.append(dependency) dependency.__reverse_runtime_deps.add(element) - element.__runtime_deps_uncached = len(element.__runtime_dependencies) + no_of_runtime_deps = len(element.__runtime_dependencies) + element.__runtime_deps_without_strict_cache_key = no_of_runtime_deps + element.__runtime_deps_uncached = no_of_runtime_deps for meta_dep in meta.build_dependencies: dependency = Element._new_from_meta(meta_dep) element.__build_dependencies.append(dependency) dependency.__reverse_build_deps.add(element) - element.__build_deps_uncached = len(element.__build_dependencies) + no_of_build_deps = len(element.__build_dependencies) + element.__build_deps_without_strict_cache_key = no_of_build_deps + element.__build_deps_uncached = no_of_build_deps element.__preflight() @@ -3042,6 +3049,9 @@ class Element(Plugin): ] self.__strict_cache_key = self._calculate_cache_key(dependencies) + if self.__strict_cache_key is not None: + self.__update_strict_cache_key_of_rdeps() + # In strict mode, the strong cache key always matches the strict cache key if context.get_strict(): self.__cache_key = self.__strict_cache_key @@ -3131,6 +3141,33 @@ class Element(Plugin): # Now we have the strong cache key, update the Artifact self.__artifact._cache_key = self.__cache_key + # __update_strict_cache_key_of_rdeps() + # + # Once an Element is given its strict cache key, immediately inform + # its reverse dependencies and see if their strict cache key can be + # obtained + # + def __update_strict_cache_key_of_rdeps(self): + if not self.__updated_strict_cache_keys_of_rdeps: + if self.__runtime_deps_without_strict_cache_key == 0 and \ + self.__strict_cache_key is not None and not self.__cache_keys_unstable: + self.__updated_strict_cache_keys_of_rdeps = True + + # Notify reverse dependencies + for rdep in self.__reverse_runtime_deps: + rdep.__runtime_deps_without_strict_cache_key -= 1 + assert not rdep.__runtime_deps_without_strict_cache_key < 0 + + if rdep.__runtime_deps_without_strict_cache_key == 0: + rdep.__update_strict_cache_key_of_rdeps() + + for rdep in self.__reverse_build_deps: + rdep.__build_deps_without_strict_cache_key -= 1 + assert not rdep.__build_deps_without_strict_cache_key < 0 + + if rdep.__build_deps_without_strict_cache_key == 0: + rdep._update_state() + def _overlap_error_detail(f, forbidden_overlap_elements, elements): if forbidden_overlap_elements: diff --git a/tests/frontend/show.py b/tests/frontend/show.py index 55691a5c6..d173fd80a 100644 --- a/tests/frontend/show.py +++ b/tests/frontend/show.py @@ -390,7 +390,7 @@ def test_fetched_junction(cli, tmpdir, datafiles, element_name, workspaced): ############################################################### # Testing recursion depth # ############################################################### -@pytest.mark.parametrize("dependency_depth", [100, 500, 1200]) +@pytest.mark.parametrize("dependency_depth", [100, 150, 1200]) def test_exceed_max_recursion_depth(cli, tmpdir, dependency_depth): project_name = "recursion-test" path = str(tmpdir) |