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