snix/third_party/nix/src/libstore/misc.cc
Vincent Ambo 1cf11317ca refactor(tvix/libutil): Mark single-argument constructors explicit
This is the clang-tidy lint 'google-explicit-constructor'.

There's a whole bunch of breakage that was introduced by this, and we
had to opt out a few types of this (esp. the string formatting crap).

In some cases minor other changes have been done to keep the code
working, instead of converting between types (e.g. an explicit
comparison operator implementation for nix::Pid).

Change-Id: I12e1ca51a6bc2c882dba81a2526b9729d26988e7
Reviewed-on: https://cl.tvl.fyi/c/depot/+/1832
Tested-by: BuildkiteCI
Reviewed-by: kanepyork <rikingcoding@gmail.com>
Reviewed-by: glittershark <grfn@gws.fyi>
2020-08-23 11:58:44 +00:00

331 lines
8.6 KiB
C++

#include <glog/logging.h>
#include "libstore/derivations.hh"
#include "libstore/globals.hh"
#include "libstore/local-store.hh"
#include "libstore/parsed-derivations.hh"
#include "libstore/store-api.hh"
#include "libutil/thread-pool.hh"
namespace nix {
void Store::computeFSClosure(const PathSet& startPaths, PathSet& paths_,
bool flipDirection, bool includeOutputs,
bool includeDerivers) {
struct State {
size_t pending;
PathSet& paths;
std::exception_ptr exc;
};
Sync<State> state_(State{0, paths_, nullptr});
std::function<void(const Path&)> enqueue;
std::condition_variable done;
enqueue = [&](const Path& path) -> void {
{
auto state(state_.lock());
if (state->exc) {
return;
}
if (state->paths.count(path) != 0u) {
return;
}
state->paths.insert(path);
state->pending++;
}
queryPathInfo(
path,
Callback<ref<ValidPathInfo>>(
[&, path](std::future<ref<ValidPathInfo>> fut) {
// FIXME: calls to isValidPath() should be async
try {
auto info = fut.get();
if (flipDirection) {
PathSet referrers;
queryReferrers(path, referrers);
for (auto& ref : referrers) {
if (ref != path) {
enqueue(ref);
}
}
if (includeOutputs) {
for (auto& i : queryValidDerivers(path)) {
enqueue(i);
}
}
if (includeDerivers && isDerivation(path)) {
for (auto& i : queryDerivationOutputs(path)) {
if (isValidPath(i) && queryPathInfo(i)->deriver == path) {
enqueue(i);
}
}
}
} else {
for (auto& ref : info->references) {
if (ref != path) {
enqueue(ref);
}
}
if (includeOutputs && isDerivation(path)) {
for (auto& i : queryDerivationOutputs(path)) {
if (isValidPath(i)) {
enqueue(i);
}
}
}
if (includeDerivers && isValidPath(info->deriver)) {
enqueue(info->deriver);
}
}
{
auto state(state_.lock());
assert(state->pending);
if (--state->pending == 0u) {
done.notify_one();
}
}
} catch (...) {
auto state(state_.lock());
if (!state->exc) {
state->exc = std::current_exception();
}
assert(state->pending);
if (--state->pending == 0u) {
done.notify_one();
}
};
}));
};
for (auto& startPath : startPaths) {
enqueue(startPath);
}
{
auto state(state_.lock());
while (state->pending != 0u) {
state.wait(done);
}
if (state->exc) {
std::rethrow_exception(state->exc);
}
}
}
void Store::computeFSClosure(const Path& startPath, PathSet& paths_,
bool flipDirection, bool includeOutputs,
bool includeDerivers) {
computeFSClosure(PathSet{startPath}, paths_, flipDirection, includeOutputs,
includeDerivers);
}
void Store::queryMissing(const PathSet& targets, PathSet& willBuild_,
PathSet& willSubstitute_, PathSet& unknown_,
unsigned long long& downloadSize_,
unsigned long long& narSize_) {
LOG(INFO) << "querying info about missing paths";
downloadSize_ = narSize_ = 0;
ThreadPool pool;
struct State {
PathSet done;
PathSet &unknown, &willSubstitute, &willBuild;
unsigned long long& downloadSize;
unsigned long long& narSize;
};
struct DrvState {
size_t left;
bool done = false;
PathSet outPaths;
explicit DrvState(size_t left) : left(left) {}
};
Sync<State> state_(State{PathSet(), unknown_, willSubstitute_, willBuild_,
downloadSize_, narSize_});
std::function<void(Path)> doPath;
auto mustBuildDrv = [&](const Path& drvPath, const Derivation& drv) {
{
auto state(state_.lock());
state->willBuild.insert(drvPath);
}
for (auto& i : drv.inputDrvs) {
pool.enqueue(
std::bind(doPath, makeDrvPathWithOutputs(i.first, i.second)));
}
};
auto checkOutput = [&](const Path& drvPath, const ref<Derivation>& drv,
const Path& outPath,
const ref<Sync<DrvState>>& drvState_) {
if (drvState_->lock()->done) {
return;
}
SubstitutablePathInfos infos;
querySubstitutablePathInfos({outPath}, infos);
if (infos.empty()) {
drvState_->lock()->done = true;
mustBuildDrv(drvPath, *drv);
} else {
{
auto drvState(drvState_->lock());
if (drvState->done) {
return;
}
assert(drvState->left);
drvState->left--;
drvState->outPaths.insert(outPath);
if (drvState->left == 0u) {
for (auto& path : drvState->outPaths) {
pool.enqueue(std::bind(doPath, path));
}
}
}
}
};
doPath = [&](const Path& path) {
{
auto state(state_.lock());
if (state->done.count(path) != 0u) {
return;
}
state->done.insert(path);
}
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(path);
if (isDerivation(i2.first)) {
if (!isValidPath(i2.first)) {
// FIXME: we could try to substitute the derivation.
auto state(state_.lock());
state->unknown.insert(path);
return;
}
Derivation drv = derivationFromPath(i2.first);
ParsedDerivation parsedDrv(i2.first, drv);
PathSet invalid;
for (auto& j : drv.outputs) {
if (wantOutput(j.first, i2.second) && !isValidPath(j.second.path)) {
invalid.insert(j.second.path);
}
}
if (invalid.empty()) {
return;
}
if (settings.useSubstitutes && parsedDrv.substitutesAllowed()) {
auto drvState = make_ref<Sync<DrvState>>(DrvState(invalid.size()));
for (auto& output : invalid) {
pool.enqueue(std::bind(checkOutput, i2.first,
make_ref<Derivation>(drv), output, drvState));
}
} else {
mustBuildDrv(i2.first, drv);
}
} else {
if (isValidPath(path)) {
return;
}
SubstitutablePathInfos infos;
querySubstitutablePathInfos({path}, infos);
if (infos.empty()) {
auto state(state_.lock());
state->unknown.insert(path);
return;
}
auto info = infos.find(path);
assert(info != infos.end());
{
auto state(state_.lock());
state->willSubstitute.insert(path);
state->downloadSize += info->second.downloadSize;
state->narSize += info->second.narSize;
}
for (auto& ref : info->second.references) {
pool.enqueue(std::bind(doPath, ref));
}
}
};
for (auto& path : targets) {
pool.enqueue(std::bind(doPath, path));
}
pool.process();
}
Paths Store::topoSortPaths(const PathSet& paths) {
Paths sorted;
PathSet visited;
PathSet parents;
std::function<void(const Path& path, const Path* parent)> dfsVisit;
dfsVisit = [&](const Path& path, const Path* parent) {
if (parents.find(path) != parents.end()) {
throw BuildError(
format("cycle detected in the references of '%1%' from '%2%'") %
path % *parent);
}
if (visited.find(path) != visited.end()) {
return;
}
visited.insert(path);
parents.insert(path);
PathSet references;
try {
references = queryPathInfo(path)->references;
} catch (InvalidPath&) {
}
for (auto& i : references) {
/* Don't traverse into paths that don't exist. That can
happen due to substitutes for non-existent paths. */
if (i != path && paths.find(i) != paths.end()) {
dfsVisit(i, &path);
}
}
sorted.push_front(path);
parents.erase(path);
};
for (auto& i : paths) {
dfsVisit(i, nullptr);
}
return sorted;
}
} // namespace nix