1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 #pragma once
18 
19 #include <folly/Synchronized.h>
20 
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`.
27  *
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.
32  */
33 namespace folly {
34 template <typename LockHolder, typename Element>
35 struct SynchronizedPtrLockedElement {
SynchronizedPtrLockedElementSynchronizedPtrLockedElement36   explicit SynchronizedPtrLockedElement(LockHolder&& holder)
37       : holder_(std::move(holder)) {}
38 
39   Element& operator*() const { return **holder_; }
40 
41   Element* operator->() const { return &**holder_; }
42 
43   explicit operator bool() const { return static_cast<bool>(*holder_); }
44 
45  private:
46   LockHolder holder_;
47 };
48 
49 template <typename PointerType, typename MutexType = SharedMutex>
50 class SynchronizedPtr {
51   using inner_type = Synchronized<PointerType, MutexType>;
52   inner_type inner_;
53 
54  public:
55   using pointer_type = PointerType;
56   using element_type = typename std::pointer_traits<pointer_type>::element_type;
57   using const_element_type = typename std::add_const<element_type>::type;
58   using read_locked_element = SynchronizedPtrLockedElement<
59       typename inner_type::ConstLockedPtr,
60       const_element_type>;
61   using write_locked_element = SynchronizedPtrLockedElement<
62       typename inner_type::LockedPtr,
63       element_type>;
64   using write_locked_pointer = typename inner_type::LockedPtr;
65 
66   template <typename... Args>
SynchronizedPtr(Args...args)67   explicit SynchronizedPtr(Args... args)
68       : inner_(std::forward<Args>(args)...) {}
69 
70   SynchronizedPtr() = default;
71   SynchronizedPtr(SynchronizedPtr const&) = default;
72   SynchronizedPtr(SynchronizedPtr&&) = default;
73   SynchronizedPtr& operator=(SynchronizedPtr const&) = default;
74   SynchronizedPtr& operator=(SynchronizedPtr&&) = default;
75 
76   // Methods to provide appropriately locked and const-qualified access to the
77   // element.
78 
rlock()79   read_locked_element rlock() const {
80     return read_locked_element(inner_.rlock());
81   }
82 
83   template <class Function>
withRLock(Function && function)84   auto withRLock(Function&& function) const {
85     return function(*rlock());
86   }
87 
wlock()88   write_locked_element wlock() { return write_locked_element(inner_.wlock()); }
89 
90   template <class Function>
withWLock(Function && function)91   auto withWLock(Function&& function) {
92     return function(*wlock());
93   }
94 
95   // Methods to provide write-locked access to the pointer. We deliberately make
96   // it difficult to get a read-locked pointer because that provides read-locked
97   // non-const access to the element, and the purpose of this class is to
98   // discourage that.
wlockPointer()99   write_locked_pointer wlockPointer() { return inner_.wlock(); }
100 
101   template <class Function>
withWLockPointer(Function && function)102   auto withWLockPointer(Function&& function) {
103     return function(*wlockPointer());
104   }
105 };
106 } // namespace folly
107