|
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