|
38 | 38 |
|
39 | 39 | #include <nlohmann/json.hpp>
|
40 | 40 | #include <boost/container/small_vector.hpp>
|
41 |
| -#include <boost/unordered/concurrent_flat_map.hpp> |
42 | 41 |
|
43 | 42 | #include "nix/util/strings-inline.hh"
|
44 | 43 |
|
@@ -265,9 +264,6 @@ EvalState::EvalState(
|
265 | 264 | , debugRepl(nullptr)
|
266 | 265 | , debugStop(false)
|
267 | 266 | , trylevel(0)
|
268 |
| - , srcToStore(make_ref<decltype(srcToStore)::element_type>()) |
269 |
| - , importResolutionCache(make_ref<decltype(importResolutionCache)::element_type>()) |
270 |
| - , fileEvalCache(make_ref<decltype(fileEvalCache)::element_type>()) |
271 | 267 | , regexCache(makeRegexCache())
|
272 | 268 | #if NIX_USE_BOEHMGC
|
273 | 269 | , valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
@@ -1030,85 +1026,63 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
|
1030 | 1026 | return &v;
|
1031 | 1027 | }
|
1032 | 1028 |
|
1033 |
| -/** |
1034 |
| - * A helper `Expr` class to lets us parse and evaluate Nix expressions |
1035 |
| - * from a thunk, ensuring that every file is parsed/evaluated only |
1036 |
| - * once (via the thunk stored in `EvalState::fileEvalCache`). |
1037 |
| - */ |
1038 |
| -struct ExprParseFile : Expr |
1039 |
| -{ |
1040 |
| - SourcePath & path; |
1041 |
| - bool mustBeTrivial; |
1042 |
| - |
1043 |
| - ExprParseFile(SourcePath & path, bool mustBeTrivial) |
1044 |
| - : path(path) |
1045 |
| - , mustBeTrivial(mustBeTrivial) |
1046 |
| - { |
1047 |
| - } |
1048 |
| - |
1049 |
| - void eval(EvalState & state, Env & env, Value & v) override |
1050 |
| - { |
1051 |
| - printTalkative("evaluating file '%s'", path); |
1052 |
| - |
1053 |
| - auto e = state.parseExprFromFile(path); |
1054 |
| - |
1055 |
| - try { |
1056 |
| - auto dts = |
1057 |
| - state.debugRepl |
1058 |
| - ? makeDebugTraceStacker( |
1059 |
| - state, *e, state.baseEnv, e->getPos(), "while evaluating the file '%s':", path.to_string()) |
1060 |
| - : nullptr; |
1061 |
| - |
1062 |
| - // Enforce that 'flake.nix' is a direct attrset, not a |
1063 |
| - // computation. |
1064 |
| - if (mustBeTrivial && !(dynamic_cast<ExprAttrs *>(e))) |
1065 |
| - state.error<EvalError>("file '%s' must be an attribute set", path).debugThrow(); |
1066 |
| - |
1067 |
| - state.eval(e, v); |
1068 |
| - } catch (Error & e) { |
1069 |
| - state.addErrorTrace(e, "while evaluating the file '%s':", path.to_string()); |
1070 |
| - throw; |
1071 |
| - } |
1072 |
| - } |
1073 |
| -}; |
1074 |
| - |
1075 | 1029 | void EvalState::evalFile(const SourcePath & path, Value & v, bool mustBeTrivial)
|
1076 | 1030 | {
|
1077 |
| - auto resolvedPath = getConcurrent(*importResolutionCache, path); |
1078 |
| - |
1079 |
| - if (!resolvedPath) { |
1080 |
| - resolvedPath = resolveExprPath(path); |
1081 |
| - importResolutionCache->emplace(path, *resolvedPath); |
| 1031 | + FileEvalCache::iterator i; |
| 1032 | + if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) { |
| 1033 | + v = i->second; |
| 1034 | + return; |
1082 | 1035 | }
|
1083 | 1036 |
|
1084 |
| - if (auto v2 = getConcurrent(*fileEvalCache, *resolvedPath)) { |
1085 |
| - forceValue(**v2, noPos); |
1086 |
| - v = **v2; |
| 1037 | + auto resolvedPath = resolveExprPath(path); |
| 1038 | + if ((i = fileEvalCache.find(resolvedPath)) != fileEvalCache.end()) { |
| 1039 | + v = i->second; |
1087 | 1040 | return;
|
1088 | 1041 | }
|
1089 | 1042 |
|
1090 |
| - Value * vExpr; |
1091 |
| - ExprParseFile expr{*resolvedPath, mustBeTrivial}; |
| 1043 | + printTalkative("evaluating file '%1%'", resolvedPath); |
| 1044 | + Expr * e = nullptr; |
1092 | 1045 |
|
1093 |
| - fileEvalCache->try_emplace_and_cvisit( |
1094 |
| - *resolvedPath, |
1095 |
| - nullptr, |
1096 |
| - [&](auto & i) { |
1097 |
| - vExpr = allocValue(); |
1098 |
| - vExpr->mkThunk(&baseEnv, &expr); |
1099 |
| - i.second = vExpr; |
1100 |
| - }, |
1101 |
| - [&](auto & i) { vExpr = i.second; }); |
| 1046 | + auto j = fileParseCache.find(resolvedPath); |
| 1047 | + if (j != fileParseCache.end()) |
| 1048 | + e = j->second; |
| 1049 | + |
| 1050 | + if (!e) |
| 1051 | + e = parseExprFromFile(resolvedPath); |
| 1052 | + |
| 1053 | + fileParseCache.emplace(resolvedPath, e); |
1102 | 1054 |
|
1103 |
| - forceValue(*vExpr, noPos); |
| 1055 | + try { |
| 1056 | + auto dts = debugRepl ? makeDebugTraceStacker( |
| 1057 | + *this, |
| 1058 | + *e, |
| 1059 | + this->baseEnv, |
| 1060 | + e->getPos(), |
| 1061 | + "while evaluating the file '%1%':", |
| 1062 | + resolvedPath.to_string()) |
| 1063 | + : nullptr; |
| 1064 | + |
| 1065 | + // Enforce that 'flake.nix' is a direct attrset, not a |
| 1066 | + // computation. |
| 1067 | + if (mustBeTrivial && !(dynamic_cast<ExprAttrs *>(e))) |
| 1068 | + error<EvalError>("file '%s' must be an attribute set", path).debugThrow(); |
| 1069 | + eval(e, v); |
| 1070 | + } catch (Error & e) { |
| 1071 | + addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath.to_string()); |
| 1072 | + throw; |
| 1073 | + } |
1104 | 1074 |
|
1105 |
| - v = *vExpr; |
| 1075 | + fileEvalCache.emplace(resolvedPath, v); |
| 1076 | + if (path != resolvedPath) |
| 1077 | + fileEvalCache.emplace(path, v); |
1106 | 1078 | }
|
1107 | 1079 |
|
1108 | 1080 | void EvalState::resetFileCache()
|
1109 | 1081 | {
|
1110 |
| - importResolutionCache->clear(); |
1111 |
| - fileEvalCache->clear(); |
| 1082 | + fileEvalCache.clear(); |
| 1083 | + fileEvalCache.rehash(0); |
| 1084 | + fileParseCache.clear(); |
| 1085 | + fileParseCache.rehash(0); |
1112 | 1086 | inputCache->clear();
|
1113 | 1087 | }
|
1114 | 1088 |
|
@@ -2427,26 +2401,24 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
|
2427 | 2401 | if (nix::isDerivation(path.path.abs()))
|
2428 | 2402 | error<EvalError>("file names are not allowed to end in '%1%'", drvExtension).debugThrow();
|
2429 | 2403 |
|
2430 |
| - auto dstPathCached = getConcurrent(*srcToStore, path); |
2431 |
| - |
2432 |
| - auto dstPath = dstPathCached ? *dstPathCached : [&]() { |
2433 |
| - auto dstPath = fetchToStore( |
| 2404 | + std::optional<StorePath> dstPath; |
| 2405 | + if (!srcToStore.cvisit(path, [&dstPath](const auto & kv) { dstPath.emplace(kv.second); })) { |
| 2406 | + dstPath.emplace(fetchToStore( |
2434 | 2407 | fetchSettings,
|
2435 | 2408 | *store,
|
2436 | 2409 | path.resolveSymlinks(SymlinkResolution::Ancestors),
|
2437 | 2410 | settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy,
|
2438 | 2411 | path.baseName(),
|
2439 | 2412 | ContentAddressMethod::Raw::NixArchive,
|
2440 | 2413 | nullptr,
|
2441 |
| - repair); |
2442 |
| - allowPath(dstPath); |
2443 |
| - srcToStore->try_emplace(path, dstPath); |
2444 |
| - printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath)); |
2445 |
| - return dstPath; |
2446 |
| - }(); |
| 2414 | + repair)); |
| 2415 | + allowPath(*dstPath); |
| 2416 | + srcToStore.try_emplace(path, *dstPath); |
| 2417 | + printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(*dstPath)); |
| 2418 | + } |
2447 | 2419 |
|
2448 |
| - context.insert(NixStringContextElem::Opaque{.path = dstPath}); |
2449 |
| - return dstPath; |
| 2420 | + context.insert(NixStringContextElem::Opaque{.path = *dstPath}); |
| 2421 | + return *dstPath; |
2450 | 2422 | }
|
2451 | 2423 |
|
2452 | 2424 | SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx)
|
|
0 commit comments