From df7e63427df5e9d002a0c1b804d7fe53d478913e Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Tue, 4 Mar 2025 23:08:34 +0100 Subject: [PATCH] libutil: add topoSortAsync Change-Id: Icad14f3e168f9577e7b99ac5f3f552f8625aebcd --- lix/libutil/topo-sort.hh | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/lix/libutil/topo-sort.hh b/lix/libutil/topo-sort.hh index c6ff856bc..a5f0bf16c 100644 --- a/lix/libutil/topo-sort.hh +++ b/lix/libutil/topo-sort.hh @@ -2,6 +2,8 @@ ///@file #include "lix/libutil/error.hh" +#include "lix/libutil/result.hh" +#include namespace nix { @@ -40,4 +42,47 @@ std::vector topoSort(std::set items, return sorted; } +template +kj::Promise>> topoSortAsync(std::set items, + std::function>>(const T &)> getChildren, + std::function makeCycleError) +try { + std::vector sorted; + std::set visited, parents; + + std::function>(const T & path, const T * parent)> dfsVisit; + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) + dfsVisit = [&](const T & path, const T * parent) -> kj::Promise> { + try { + if (parents.count(path)) throw makeCycleError(path, *parent); + + if (!visited.insert(path).second) co_return result::success(); + parents.insert(path); + + std::set references = TRY_AWAIT(getChildren(path)); + + for (auto & i : references) + /* Don't traverse into items that don't exist in our starting set. */ + if (i != path && items.count(i)) + TRY_AWAIT(dfsVisit(i, &path)); + + sorted.push_back(path); + parents.erase(path); + co_return result::success(); + } catch (...) { + co_return result::current_exception(); + } + }; + + for (auto & i : items) + TRY_AWAIT(dfsVisit(i, nullptr)); + + std::reverse(sorted.begin(), sorted.end()); + + co_return sorted; +} catch (...) { + co_return result::current_exception(); +} + }