/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * 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 /* `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>>` consider using * `SynchronizedPtr>`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&` respectively, and return the callback's result. */ namespace folly { template 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(*holder_); } private: LockHolder holder_; }; template class SynchronizedPtr { using inner_type = Synchronized; inner_type inner_; public: using pointer_type = PointerType; using element_type = typename std::pointer_traits::element_type; using const_element_type = typename std::add_const::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 explicit SynchronizedPtr(Args... args) : inner_(std::forward(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 auto withRLock(Function&& function) const { return function(*rlock()); } write_locked_element wlock() { return write_locked_element(inner_.wlock()); } template 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 auto withWLockPointer(Function&& function) { return function(*wlockPointer()); } }; } // namespace folly