#include "goal.hh" #include "async-collect.hh" #include "worker.hh" #include #include namespace nix { void Goal::trace(std::string_view s) { debug("%1%: %2%", name, s); } kj::Promise Goal::waitForAWhile() { trace("wait for a while"); /* If we are polling goals that are waiting for a lock, then wake up after a few seconds at most. */ return worker.aio.provider->getTimer().afterDelay(settings.pollInterval.get() * kj::SECONDS); } kj::Promise> Goal::work() noexcept try { // always clear the slot token, no matter what happens. not doing this // can cause builds to get stuck on exceptions (or other early exist). // ideally we'd use scoped slot tokens instead of keeping them in some // goal member variable, but we cannot do this yet for legacy reasons. KJ_DEFER({ slotToken = {}; }); BOOST_OUTCOME_CO_TRY(auto result, co_await workImpl()); trace("done"); cleanup(); co_return std::move(result); } catch (...) { co_return result::failure(std::current_exception()); } kj::Promise> Goal::waitForGoals(kj::Array>>> dependencies) noexcept try { auto left = dependencies.size(); for (auto & [dep, p] : dependencies) { p = p.then([this, dep, &left](auto _result) -> Result { BOOST_OUTCOME_TRY(auto result, _result); left--; trace(fmt("waitee '%s' done; %d left", dep->name, left)); if (result.exitCode != Goal::ecSuccess) ++nrFailed; if (result.exitCode == Goal::ecNoSubstituters) ++nrNoSubstituters; if (result.exitCode == Goal::ecIncompleteClosure) ++nrIncompleteClosure; return std::move(result); }).eagerlyEvaluate(nullptr); } auto collectDeps = asyncCollect(std::move(dependencies)); while (auto item = co_await collectDeps.next()) { auto & [dep, _result] = *item; BOOST_OUTCOME_CO_TRY(auto result, _result); waiteeDone(dep); if (result.exitCode == ecFailed && !settings.keepGoing) { co_return result::success(); } } co_return result::success(); } catch (...) { co_return result::failure(std::current_exception()); } }