Skip to content

Commit e95503c

Browse files
committed
libutil: Make PosixSourceAccessor update mtime only when needed
Typically PosixSourceAccessor can be used from multiple threads, but mtime is not updated atomically (i.e. with compare_exchange_weak), so mtime gets raced. It's only needed in dumpPathAndGetMtime and mtime tracking can be gated behind that. Also start using getLastModified interface instead of dynamic casts.
1 parent 3645671 commit e95503c

File tree

3 files changed

+27
-11
lines changed

3 files changed

+27
-11
lines changed

src/libutil/archive.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@ void SourceAccessor::dumpPath(const CanonPath & path, Sink & sink, PathFilter &
103103

104104
time_t dumpPathAndGetMtime(const Path & path, Sink & sink, PathFilter & filter)
105105
{
106-
auto path2 = PosixSourceAccessor::createAtRoot(path);
106+
auto path2 = PosixSourceAccessor::createAtRoot(path, /*trackLastModified=*/true);
107107
path2.dumpPath(sink, filter);
108-
return path2.accessor.dynamic_pointer_cast<PosixSourceAccessor>()->mtime;
108+
return path2.accessor->getLastModified().value();
109109
}
110110

111111
void dumpPath(const Path & path, Sink & sink, PathFilter & filter)

src/libutil/include/nix/util/posix-source-accessor.hh

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ struct SourcePath;
99
/**
1010
* A source accessor that uses the Unix filesystem.
1111
*/
12-
struct PosixSourceAccessor : virtual SourceAccessor
12+
class PosixSourceAccessor : virtual public SourceAccessor
1313
{
1414
/**
1515
* Optional root path to prefix all operations into the native file
@@ -18,8 +18,12 @@ struct PosixSourceAccessor : virtual SourceAccessor
1818
*/
1919
const std::filesystem::path root;
2020

21+
const bool trackLastModified = false;
22+
23+
public:
24+
2125
PosixSourceAccessor();
22-
PosixSourceAccessor(std::filesystem::path && root);
26+
PosixSourceAccessor(std::filesystem::path && root, bool trackLastModified = false);
2327

2428
/**
2529
* The most recent mtime seen by lstat(). This is a hack to
@@ -43,6 +47,9 @@ struct PosixSourceAccessor : virtual SourceAccessor
4347
* Create a `PosixSourceAccessor` and `SourcePath` corresponding to
4448
* some native path.
4549
*
50+
* @param Whether the accessor should return a non-null getLastModified.
51+
* When true the accessor must be used only by a single thread.
52+
*
4653
* The `PosixSourceAccessor` is rooted as far up the tree as
4754
* possible, (e.g. on Windows it could scoped to a drive like
4855
* `C:\`). This allows more `..` parent accessing to work.
@@ -64,7 +71,12 @@ struct PosixSourceAccessor : virtual SourceAccessor
6471
* and
6572
* [`std::filesystem::path::relative_path`](https://en.cppreference.com/w/cpp/filesystem/path/relative_path).
6673
*/
67-
static SourcePath createAtRoot(const std::filesystem::path & path);
74+
static SourcePath createAtRoot(const std::filesystem::path & path, bool trackLastModified = false);
75+
76+
std::optional<std::time_t> getLastModified() override
77+
{
78+
return trackLastModified ? std::optional{mtime} : std::nullopt;
79+
}
6880

6981
private:
7082

src/libutil/posix-source-accessor.cc

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77

88
namespace nix {
99

10-
PosixSourceAccessor::PosixSourceAccessor(std::filesystem::path && argRoot)
10+
PosixSourceAccessor::PosixSourceAccessor(std::filesystem::path && argRoot, bool trackLastModified)
1111
: root(std::move(argRoot))
12+
, trackLastModified(trackLastModified)
1213
{
1314
assert(root.empty() || root.is_absolute());
1415
displayPrefix = root.string();
@@ -19,11 +20,11 @@ PosixSourceAccessor::PosixSourceAccessor()
1920
{
2021
}
2122

22-
SourcePath PosixSourceAccessor::createAtRoot(const std::filesystem::path & path)
23+
SourcePath PosixSourceAccessor::createAtRoot(const std::filesystem::path & path, bool trackLastModified)
2324
{
2425
std::filesystem::path path2 = absPath(path);
2526
return {
26-
make_ref<PosixSourceAccessor>(path2.root_path()),
27+
make_ref<PosixSourceAccessor>(path2.root_path(), trackLastModified),
2728
CanonPath{path2.relative_path().string()},
2829
};
2930
}
@@ -114,9 +115,12 @@ std::optional<SourceAccessor::Stat> PosixSourceAccessor::maybeLstat(const CanonP
114115
auto st = cachedLstat(path);
115116
if (!st)
116117
return std::nullopt;
117-
// This makes the accessor thread-unsafe, but we only seem to use the actual value in a single threaded context in
118-
// `src/libfetchers/path.cc`.
119-
mtime = std::max(mtime, st->st_mtime);
118+
119+
/* The contract is that trackLastModified implies that the caller uses the accessor
120+
from a single thread. Thus this is not a CAS loop. */
121+
if (trackLastModified)
122+
mtime = std::max(mtime, st->st_mtime);
123+
120124
return Stat{
121125
.type = S_ISREG(st->st_mode) ? tRegular
122126
: S_ISDIR(st->st_mode) ? tDirectory

0 commit comments

Comments
 (0)