Skip to content

Commit 727cbf8

Browse files
authored
Merge pull request #197 from DeterminateSystems/more-concurrent-boost
More use of boost::concurrent_flat_map and a correctness fix
2 parents a3b3220 + bb18a48 commit 727cbf8

File tree

6 files changed

+67
-34
lines changed

6 files changed

+67
-34
lines changed

src/libexpr/eval.cc

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
#include <nlohmann/json.hpp>
4242
#include <boost/container/small_vector.hpp>
43+
#include <boost/unordered/concurrent_flat_map.hpp>
4344

4445
#ifndef _WIN32 // TODO use portable implementation
4546
# include <sys/resource.h>
@@ -213,6 +214,27 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
213214

214215
static constexpr size_t BASE_ENV_SIZE = 128;
215216

217+
struct EvalState::SrcToStore
218+
{
219+
boost::concurrent_flat_map<SourcePath, StorePath> inner;
220+
};
221+
222+
struct EvalState::ImportResolutionCache
223+
{
224+
boost::concurrent_flat_map<SourcePath, SourcePath> inner;
225+
};
226+
227+
struct EvalState::FileEvalCache
228+
{
229+
boost::concurrent_flat_map<
230+
SourcePath,
231+
Value *,
232+
std::hash<SourcePath>,
233+
std::equal_to<SourcePath>,
234+
traceable_allocator<std::pair<const SourcePath, Value *>>>
235+
inner;
236+
};
237+
216238
EvalState::EvalState(
217239
const LookupPath & lookupPathFromArguments,
218240
ref<Store> store,
@@ -344,6 +366,9 @@ EvalState::EvalState(
344366
, debugStop(false)
345367
, trylevel(0)
346368
, asyncPathWriter(AsyncPathWriter::make(store))
369+
, srcToStore(make_ref<SrcToStore>())
370+
, importResolutionCache(make_ref<ImportResolutionCache>())
371+
, fileEvalCache(make_ref<FileEvalCache>())
347372
, regexCache(makeRegexCache())
348373
#if NIX_USE_BOEHMGC
349374
, baseEnvP(std::allocate_shared<Env *>(traceable_allocator<Env *>(), &allocEnv(BASE_ENV_SIZE)))
@@ -1168,29 +1193,31 @@ struct ExprParseFile : Expr
11681193

11691194
void EvalState::evalFile(const SourcePath & path, Value & v, bool mustBeTrivial)
11701195
{
1171-
auto resolvedPath = getOptional(*importResolutionCache.readLock(), path);
1196+
auto resolvedPath = getConcurrent(importResolutionCache->inner, path);
11721197

11731198
if (!resolvedPath) {
11741199
resolvedPath = resolveExprPath(path);
1175-
importResolutionCache.lock()->emplace(path, *resolvedPath);
1200+
importResolutionCache->inner.emplace(path, *resolvedPath);
11761201
}
11771202

1178-
if (auto v2 = get(*fileEvalCache.readLock(), *resolvedPath)) {
1179-
forceValue(*const_cast<Value *>(v2), noPos);
1180-
v = *v2;
1203+
if (auto v2 = getConcurrent(fileEvalCache->inner, *resolvedPath)) {
1204+
forceValue(**v2, noPos);
1205+
v = **v2;
11811206
return;
11821207
}
11831208

11841209
Value * vExpr;
11851210
ExprParseFile expr{*resolvedPath, mustBeTrivial};
11861211

1187-
{
1188-
auto cache(fileEvalCache.lock());
1189-
auto [i, inserted] = cache->try_emplace(*resolvedPath);
1190-
if (inserted)
1191-
i->second.mkThunk(&baseEnv, &expr);
1192-
vExpr = &i->second;
1193-
}
1212+
fileEvalCache->inner.try_emplace_and_cvisit(
1213+
*resolvedPath,
1214+
nullptr,
1215+
[&](auto & i) {
1216+
vExpr = allocValue();
1217+
vExpr->mkThunk(&baseEnv, &expr);
1218+
i.second = vExpr;
1219+
},
1220+
[&](auto & i) { vExpr = i.second; });
11941221

11951222
forceValue(*vExpr, noPos);
11961223

@@ -1199,7 +1226,7 @@ void EvalState::evalFile(const SourcePath & path, Value & v, bool mustBeTrivial)
11991226

12001227
void EvalState::resetFileCache()
12011228
{
1202-
fileEvalCache.lock()->clear();
1229+
fileEvalCache->inner.clear();
12031230
inputCache->clear();
12041231
}
12051232

@@ -2499,7 +2526,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
24992526
if (nix::isDerivation(path.path.abs()))
25002527
error<EvalError>("file names are not allowed to end in '%1%'", drvExtension).debugThrow();
25012528

2502-
auto dstPathCached = get(*srcToStore.lock(), path);
2529+
auto dstPathCached = getConcurrent(srcToStore->inner, path);
25032530

25042531
auto dstPath = dstPathCached ? *dstPathCached : [&]() {
25052532
auto dstPath = fetchToStore(
@@ -2512,7 +2539,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
25122539
nullptr,
25132540
repair);
25142541
allowPath(dstPath);
2515-
srcToStore.lock()->try_emplace(path, dstPath);
2542+
srcToStore->inner.try_emplace(path, dstPath);
25162543
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath));
25172544
return dstPath;
25182545
}();

src/libexpr/include/nix/expr/eval.hh

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -371,25 +371,21 @@ private:
371371

372372
/* Cache for calls to addToStore(); maps source paths to the store
373373
paths. */
374-
Sync<std::unordered_map<SourcePath, StorePath>> srcToStore;
374+
struct SrcToStore;
375+
ref<SrcToStore> srcToStore;
375376

376377
/**
377378
* A cache that maps paths to "resolved" paths for importing Nix
378379
* expressions, i.e. `/foo` to `/foo/default.nix`.
379380
*/
380-
SharedSync<std::unordered_map<SourcePath, SourcePath>> importResolutionCache;
381+
struct ImportResolutionCache;
382+
ref<ImportResolutionCache> importResolutionCache;
381383

382384
/**
383385
* A cache from resolved paths to values.
384386
*/
385-
typedef std::unordered_map<
386-
SourcePath,
387-
Value,
388-
std::hash<SourcePath>,
389-
std::equal_to<SourcePath>,
390-
traceable_allocator<std::pair<const SourcePath, Value>>>
391-
FileEvalCache;
392-
SharedSync<FileEvalCache> fileEvalCache;
387+
struct FileEvalCache;
388+
ref<FileEvalCache> fileEvalCache;
393389

394390
/**
395391
* Associate source positions of certain AST nodes with their preceding doc comment, if they have one.

src/libutil/include/nix/util/source-path.hh

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,21 @@ struct SourcePath
119119

120120
std::ostream & operator<<(std::ostream & str, const SourcePath & path);
121121

122+
inline std::size_t hash_value(const SourcePath & path)
123+
{
124+
std::size_t hash = 0;
125+
boost::hash_combine(hash, path.accessor->number);
126+
boost::hash_combine(hash, path.path);
127+
return hash;
128+
}
129+
122130
} // namespace nix
123131

124132
template<>
125133
struct std::hash<nix::SourcePath>
126134
{
127135
std::size_t operator()(const nix::SourcePath & s) const noexcept
128136
{
129-
std::size_t hash = 0;
130-
hash_combine(hash, s.accessor->number, s.path);
131-
return hash;
137+
return nix::hash_value(s);
132138
}
133139
};

src/libutil/include/nix/util/util.hh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,14 @@ std::optional<typename T::mapped_type> getOptional(const T & map, const typename
227227
return {i->second};
228228
}
229229

230+
template<class T>
231+
std::optional<typename T::mapped_type> getConcurrent(const T & map, const typename T::key_type & key)
232+
{
233+
std::optional<typename T::mapped_type> res;
234+
map.cvisit(key, [&](auto & x) { res = x.second; });
235+
return res;
236+
}
237+
230238
/**
231239
* Get a value for the specified key from an associate container, or a default value if the key isn't present.
232240
*/

src/libutil/mounted-source-accessor.cc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,7 @@ struct MountedSourceAccessorImpl : MountedSourceAccessor
8686

8787
std::shared_ptr<SourceAccessor> getMount(CanonPath mountPoint) override
8888
{
89-
std::optional<ref<SourceAccessor>> res;
90-
mounts.cvisit(mountPoint, [&](auto & x) { res = x.second; });
91-
if (res)
89+
if (auto res = getConcurrent(mounts, mountPoint))
9290
return *res;
9391
else
9492
return nullptr;

src/libutil/posix-source-accessor.cc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,7 @@ std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & pa
9595
// former is not hashable on libc++.
9696
Path absPath = makeAbsPath(path).string();
9797

98-
std::optional<Cache::mapped_type> res;
99-
cache.cvisit(absPath, [&](auto & x) { res.emplace(x.second); });
100-
if (res)
98+
if (auto res = getConcurrent(cache, absPath))
10199
return *res;
102100

103101
auto st = nix::maybeLstat(absPath.c_str());

0 commit comments

Comments
 (0)