2 * Copyright 2017-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <folly/Synchronized.h>
21 /* `SynchronizedPtr` is a variation on the `Synchronized` idea that's useful for
22 * some cases where you want to protect a pointed-to object (or an object within
23 * some pointer-like wrapper). If you would otherwise need to use
24 * `Synchronized<smart_ptr<Synchronized<T>>>` consider using
25 * `SynchronizedPtr<smart_ptr<T>>`as it is a bit easier to use and it works when
26 * you want the `T` object at runtime to actually a subclass of `T`.
28 * You can access the contained `T` with `.rlock()`, and `.wlock()`, and the
29 * pointer or pointer-like wrapper with `.wlockPointer()`. The corresponding
30 * `with...` methods take a callback, invoke it with a `T const&`, `T&` or
31 * `smart_ptr<T>&` respectively, and return the callback's result.
34 template <typename LockHolder, typename Element>
35 struct SynchronizedPtrLockedElement {
36 explicit SynchronizedPtrLockedElement(LockHolder&& holder)
37 : holder_(std::move(holder)) {}
39 Element& operator*() const {
43 Element* operator->() const {
47 explicit operator bool() const {
48 return static_cast<bool>(*holder_);
55 template <typename PointerType, typename MutexType = SharedMutex>
56 class SynchronizedPtr {
57 using inner_type = Synchronized<PointerType, MutexType>;
61 using pointer_type = PointerType;
62 using element_type = typename std::pointer_traits<pointer_type>::element_type;
63 using const_element_type = typename std::add_const<element_type>::type;
64 using read_locked_element = SynchronizedPtrLockedElement<
65 typename inner_type::ConstLockedPtr,
67 using write_locked_element = SynchronizedPtrLockedElement<
68 typename inner_type::LockedPtr,
70 using write_locked_pointer = typename inner_type::LockedPtr;
72 template <typename... Args>
73 explicit SynchronizedPtr(Args... args)
74 : inner_(std::forward<Args>(args)...) {}
76 SynchronizedPtr() = default;
77 SynchronizedPtr(SynchronizedPtr const&) = default;
78 SynchronizedPtr(SynchronizedPtr&&) = default;
79 SynchronizedPtr& operator=(SynchronizedPtr const&) = default;
80 SynchronizedPtr& operator=(SynchronizedPtr&&) = default;
82 // Methods to provide appropriately locked and const-qualified access to the
85 read_locked_element rlock() const {
86 return read_locked_element(inner_.rlock());
89 template <class Function>
90 auto withRLock(Function&& function) const {
91 return function(*rlock());
94 write_locked_element wlock() {
95 return write_locked_element(inner_.wlock());
98 template <class Function>
99 auto withWLock(Function&& function) {
100 return function(*wlock());
103 // Methods to provide write-locked access to the pointer. We deliberately make
104 // it difficult to get a read-locked pointer because that provides read-locked
105 // non-const access to the element, and the purpose of this class is to
107 write_locked_pointer wlockPointer() {
108 return inner_.wlock();
111 template <class Function>
112 auto withWLockPointer(Function&& function) {
113 return function(*wlockPointer());