diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index bcf453cba..35047f89e 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -90,14 +90,14 @@ bool PosixSourceAccessor::pathExists(const CanonPath & path) std::optional PosixSourceAccessor::cachedLstat(const CanonPath & path) { - static Sync>> _cache; + static SharedSync>> _cache; // Note: we convert std::filesystem::path to Path because the // former is not hashable on libc++. Path absPath = makeAbsPath(path).string(); { - auto cache(_cache.lock()); + auto cache(_cache.read()); auto i = cache->find(absPath); if (i != cache->end()) return i->second; } diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh index 47e4512b1..20dd6ee52 100644 --- a/src/libutil/sync.hh +++ b/src/libutil/sync.hh @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -24,8 +25,8 @@ namespace nix { * Here, "data" is automatically unlocked when "data_" goes out of * scope. */ -template -class Sync +template +class SyncBase { private: M mutex; @@ -33,23 +34,22 @@ private: public: - Sync() { } - Sync(const T & data) : data(data) { } - Sync(T && data) noexcept : data(std::move(data)) { } + SyncBase() { } + SyncBase(const T & data) : data(data) { } + SyncBase(T && data) noexcept : data(std::move(data)) { } + template class Lock { - private: - Sync * s; - std::unique_lock lk; - friend Sync; - Lock(Sync * s) : s(s), lk(s->mutex) { } + protected: + SyncBase * s; + L lk; + friend SyncBase; + Lock(SyncBase * s) : s(s), lk(s->mutex) { } public: Lock(Lock && l) : s(l.s) { abort(); } Lock(const Lock & l) = delete; ~Lock() { } - T * operator -> () { return &s->data; } - T & operator * () { return s->data; } void wait(std::condition_variable & cv) { @@ -83,7 +83,34 @@ public: } }; - Lock lock() { return Lock(this); } + struct WriteLock : Lock + { + T * operator -> () { return &WriteLock::s->data; } + T & operator * () { return WriteLock::s->data; } + }; + + /** + * Acquire write (exclusive) access to the inner value. + */ + WriteLock lock() { return WriteLock(this); } + + struct ReadLock : Lock + { + const T * operator -> () { return &ReadLock::s->data; } + const T & operator * () { return ReadLock::s->data; } + }; + + /** + * Acquire read access to the inner value. When using + * `std::shared_mutex`, this will use a shared lock. + */ + ReadLock read() const { return ReadLock(const_cast(this)); } }; +template +using Sync = SyncBase, std::unique_lock>; + +template +using SharedSync = SyncBase, std::shared_lock>; + }