]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Introduce LockGuarded, a lock-protected data
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 16 Apr 2021 13:34:50 +0000 (15:34 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 17 Aug 2021 12:04:39 +0000 (14:04 +0200)
The general idea has been borrowed from Rust's locks: instead of
defining two objects, the one to be protected, T, and the lock, we
define a single LockGuarded<T> object which contains the object.
That provides two big advantages:
- it is immediately clear which data is protected by the lock
- that data simply can't be accessed without holding the lock.

pdns/lock.hh

index 0644502f87122f9cb2d665b49e03e80660d812c9..ed4b07a6c38b50fff45838562f80f4b00b120a7d 100644 (file)
@@ -148,3 +148,82 @@ private:
 
   std::unique_lock<std::shared_mutex> d_lock;
 };
+
+template <typename T>
+class LockGuardedHolder
+{
+public:
+  explicit LockGuardedHolder(T& value, std::mutex& mutex): d_lock(mutex), d_value(value)
+  {
+  }
+
+  T& operator*() const noexcept {
+    return d_value;
+  }
+
+  T* operator->() const noexcept {
+    return &d_value;
+  }
+
+private:
+  std::lock_guard<std::mutex> d_lock;
+  T& d_value;
+};
+
+template <typename T>
+class LockGuardedTryHolder
+{
+public:
+  explicit LockGuardedTryHolder(T& value, std::mutex& mutex): d_lock(mutex, std::try_to_lock), d_value(value)
+  {
+  }
+
+  T& operator*() const {
+    if (!owns_lock()) {
+      throw std::runtime_error("Trying to access data protected by a mutex while the lock has not been acquired");
+    }
+    return d_value;
+  }
+
+  T* operator->() const {
+    if (!owns_lock()) {
+      throw std::runtime_error("Trying to access data protected by a mutex while the lock has not been acquired");
+    }
+    return &d_value;
+  }
+
+  operator bool() const noexcept {
+    return d_lock.owns_lock();
+  }
+
+  bool owns_lock() const noexcept {
+    return d_lock.owns_lock();
+  }
+
+private:
+  std::unique_lock<std::mutex> d_lock;
+  T& d_value;
+};
+
+template <typename T>
+class LockGuarded
+{
+public:
+  explicit LockGuarded(T value): d_value(std::move(value))
+  {
+  }
+
+  LockGuardedTryHolder<T> try_lock()
+  {
+    return LockGuardedTryHolder<T>(d_value, d_mutex);
+  }
+
+  LockGuardedHolder<T> lock()
+  {
+    return LockGuardedHolder<T>(d_value, d_mutex);
+  }
+
+private:
+  std::mutex d_mutex;
+  T d_value;
+};