SynchronizedPtr
[folly.git] / folly / SynchronizedPtr.h
diff --git a/folly/SynchronizedPtr.h b/folly/SynchronizedPtr.h
new file mode 100644 (file)
index 0000000..9a9ea12
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2004-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Synchronized.h>
+
+/* `SynchronizedPtr` is a variation on the `Synchronized` idea that's useful for
+ * some cases where you want to protect a pointed-to object (or an object within
+ * some pointer-like wrapper). If you would otherwise need to use
+ * `Synchronized<smart_ptr<Synchronized<T>>>` consider using
+ * `SynchronizedPtr<smart_ptr<T>>`as it is a bit easier to use and it works when
+ * you want the `T` object at runtime to actually a subclass of `T`.
+ *
+ * You can access the contained `T` with `.rlock()`, and `.wlock()`, and the
+ * pointer or pointer-like wrapper with `.wlockPointer()`. The corresponding
+ * `with...` methods take a callback, invoke it with a `T const&`, `T&` or
+ * `smart_ptr<T>&` respectively, and return the callback's result.
+ */
+namespace folly {
+template <typename LockHolder, typename Element>
+struct SynchronizedPtrLockedElement {
+  explicit SynchronizedPtrLockedElement(LockHolder&& holder)
+      : holder_(std::move(holder)) {}
+
+  Element& operator*() const {
+    return **holder_;
+  }
+
+  Element* operator->() const {
+    return &**holder_;
+  }
+
+  explicit operator bool() const {
+    return static_cast<bool>(*holder_);
+  }
+
+ private:
+  LockHolder holder_;
+};
+
+template <typename PointerType, typename MutexType = SharedMutex>
+class SynchronizedPtr {
+  using inner_type = Synchronized<PointerType, MutexType>;
+  inner_type inner_;
+
+ public:
+  using pointer_type = PointerType;
+  using element_type = typename std::pointer_traits<pointer_type>::element_type;
+  using const_element_type = typename std::add_const<element_type>::type;
+  using read_locked_element = SynchronizedPtrLockedElement<
+      typename inner_type::ConstLockedPtr,
+      const_element_type>;
+  using write_locked_element = SynchronizedPtrLockedElement<
+      typename inner_type::LockedPtr,
+      element_type>;
+  using write_locked_pointer = typename inner_type::LockedPtr;
+
+  template <typename... Args>
+  explicit SynchronizedPtr(Args... args)
+      : inner_(std::forward<Args>(args)...) {}
+
+  SynchronizedPtr() = default;
+  SynchronizedPtr(SynchronizedPtr const&) = default;
+  SynchronizedPtr(SynchronizedPtr&&) = default;
+  SynchronizedPtr& operator=(SynchronizedPtr const&) = default;
+  SynchronizedPtr& operator=(SynchronizedPtr&&) = default;
+
+  // Methods to provide appropriately locked and const-qualified access to the
+  // element.
+
+  read_locked_element rlock() const {
+    return read_locked_element(inner_.rlock());
+  }
+
+  template <class Function>
+  auto withRLock(Function&& function) const {
+    return function(*rlock());
+  }
+
+  write_locked_element wlock() {
+    return write_locked_element(inner_.wlock());
+  }
+
+  template <class Function>
+  auto withWLock(Function&& function) {
+    return function(*wlock());
+  }
+
+  // Methods to provide write-locked access to the pointer. We deliberately make
+  // it difficult to get a read-locked pointer because that provides read-locked
+  // non-const access to the element, and the purpose of this class is to
+  // discourage that.
+  write_locked_pointer wlockPointer() {
+    return inner_.wlock();
+  }
+
+  template <class Function>
+  auto withWLockPointer(Function&& function) {
+    return function(*wlockPointer());
+  }
+};
+} // namespace folly