Merge pull request #10868 from DeterminateSystems/shared-sync

Add SharedSync class
This commit is contained in:
John Ericson 2024-06-06 10:11:36 -04:00 committed by GitHub
commit dd46ed85eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 42 additions and 15 deletions

View file

@ -90,14 +90,14 @@ bool PosixSourceAccessor::pathExists(const CanonPath & path)
std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & path) std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & path)
{ {
static Sync<std::unordered_map<Path, std::optional<struct stat>>> _cache; static SharedSync<std::unordered_map<Path, std::optional<struct stat>>> _cache;
// Note: we convert std::filesystem::path to Path because the // Note: we convert std::filesystem::path to Path because the
// former is not hashable on libc++. // former is not hashable on libc++.
Path absPath = makeAbsPath(path).string(); Path absPath = makeAbsPath(path).string();
{ {
auto cache(_cache.lock()); auto cache(_cache.read());
auto i = cache->find(absPath); auto i = cache->find(absPath);
if (i != cache->end()) return i->second; if (i != cache->end()) return i->second;
} }

View file

@ -3,6 +3,7 @@
#include <cstdlib> #include <cstdlib>
#include <mutex> #include <mutex>
#include <shared_mutex>
#include <condition_variable> #include <condition_variable>
#include <cassert> #include <cassert>
@ -24,8 +25,8 @@ namespace nix {
* Here, "data" is automatically unlocked when "data_" goes out of * Here, "data" is automatically unlocked when "data_" goes out of
* scope. * scope.
*/ */
template<class T, class M = std::mutex> template<class T, class M, class WL, class RL>
class Sync class SyncBase
{ {
private: private:
M mutex; M mutex;
@ -33,23 +34,22 @@ private:
public: public:
Sync() { } SyncBase() { }
Sync(const T & data) : data(data) { } SyncBase(const T & data) : data(data) { }
Sync(T && data) noexcept : data(std::move(data)) { } SyncBase(T && data) noexcept : data(std::move(data)) { }
template<class L>
class Lock class Lock
{ {
private: protected:
Sync * s; SyncBase * s;
std::unique_lock<M> lk; L lk;
friend Sync; friend SyncBase;
Lock(Sync * s) : s(s), lk(s->mutex) { } Lock(SyncBase * s) : s(s), lk(s->mutex) { }
public: public:
Lock(Lock && l) : s(l.s) { abort(); } Lock(Lock && l) : s(l.s) { abort(); }
Lock(const Lock & l) = delete; Lock(const Lock & l) = delete;
~Lock() { } ~Lock() { }
T * operator -> () { return &s->data; }
T & operator * () { return s->data; }
void wait(std::condition_variable & cv) void wait(std::condition_variable & cv)
{ {
@ -83,7 +83,34 @@ public:
} }
}; };
Lock lock() { return Lock(this); } struct WriteLock : Lock<WL>
{
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<RL>
{
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<SyncBase *>(this)); }
};
template<class T>
using Sync = SyncBase<T, std::mutex, std::unique_lock<std::mutex>, std::unique_lock<std::mutex>>;
template<class T>
using SharedSync = SyncBase<T, std::shared_mutex, std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex>>;
} }