/* * 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 <chrono> #include <mutex> #include <shared_mutex> #include <system_error> #include <type_traits> #include <folly/Portability.h> #include <folly/Traits.h> #include <folly/functional/Invoke.h> #include <folly/lang/Exception.h> #include <folly/lang/Hint.h> namespace folly { access // namespace access namespace detail { // A lock base class with a mostly-complete implementation suitable for either // unique, shared, or upgrade lock base classes. However, each particular base // class specific to each lock category must still be its own class to avoid // overly permissive overloads of member and free swap. template <typename Mutex, typename Policy> class lock_base { … }; template <typename Mutex, typename Policy> class lock_guard_base : unsafe_for_async_usage_if<!is_coro_aware_mutex_v<Mutex>> { … }; struct lock_policy_unique { … }; struct lock_policy_shared { … }; struct lock_policy_upgrade { … }; lock_policy_hybrid; lock_base_unique; lock_base_shared; lock_base_upgrade; lock_base_hybrid; } // namespace detail // unique_lock_base // // A lock-holder base which holds exclusive locks, usable with any mutex type. // // Works with both lockable mutex types and lockable-with-state mutex types. // // When defining lockable-with-state mutex types, specialize std::unique_lock // to derive this. See the example with upgrade_lock. // // A lockable-with-state mutex type is signalled by the return type of mutex // member function lock. Members try_lock, try_lock_for, and try_lock_until // all return this type and member unlock accepts this type. template <typename Mutex> class unique_lock_base : public detail::lock_base_unique<Mutex> { … }; // shared_lock_base // // A lock-holder base which holds shared locks, usable with any shared mutex // type. // // Works with both shared-lockable mutex types and shared-lockable-with-state // mutex types. // // When defining shared-lockable-with-state mutex types, specialize // std::shared_lock to derive this. See the example with upgrade_lock. // // A shared-lockable-with-state mutex type is signalled by the return type of // mutex member function lock_shared. Members try_lock_shared, // try_lock_shared_for, and try_lock_shared_until all return this type and // member unlock_shared accepts this type. Likewise for mutex member // transition functions. template <typename Mutex> class shared_lock_base : public detail::lock_base_shared<Mutex> { … }; // upgrade_lock_base // // A lock-holder base which holds upgrade locks, usable with any upgrade mutex // type. // // Works with both upgrade-lockable mutex types and upgrade-lockable-with-state // mutex types. // // There are no use-cases except the one below. // // An upgrade-lockable-with-state mutex type is signalled by the return type of // mutex member function lock_upgrade. Members try_lock_upgrade, // try_lock_upgrade_for, and try_lock_upgrade_until all return this type and // member unlock_upgrade accepts this type. Likewise for mutex member // transition functions. template <typename Mutex> class upgrade_lock_base : public detail::lock_base_upgrade<Mutex> { … }; // hybrid_lock_base // // A lock-holder base which holds shared locks for shared mutex types or // exclusive locks otherwise. // // See unique_lock_base and shared_lock_base. template <typename Mutex> class hybrid_lock_base : public detail::lock_base_hybrid<Mutex> { … }; // unique_lock // // Alias to std::unique_lock. unique_lock; // shared_lock // // Alias to std::shared_lock. shared_lock; // upgrade_lock // // A lock-holder type which holds upgrade locks, usable with any upgrade mutex // type. An upgrade mutex is a shared mutex which supports the upgrade state. // // Works with both upgrade-lockable mutex types and upgrade-lockable-with-state // mutex types. // // Upgrade locks are not useful by themselves; they are primarily useful since // upgrade locks may be transitioned atomically to exclusive locks. This lock- // holder type works with the transition_to_... functions below to facilitate // atomic transition from ugprade lock to exclusive lock. template <typename Mutex> class upgrade_lock : public upgrade_lock_base<Mutex> { … }; template <typename Mutex, typename... A> explicit upgrade_lock(Mutex&, A const&...) -> upgrade_lock<Mutex>; // hybrid_lock // // A lock-holder type which holds shared locks for shared mutex types or // exclusive locks otherwise. // // See unique_lock and shared_lock. template <typename Mutex> class hybrid_lock : public hybrid_lock_base<Mutex> { … }; template <typename Mutex, typename... A> explicit hybrid_lock(Mutex&, A const&...) -> hybrid_lock<Mutex>; // lock_guard_base // // A lock-guard which holds exclusive locks, usable with any mutex type. // // Works with both lockable mutex types and lockable-with-state mutex types. // // When defining lockable-with-state mutex types, specialize std::lock_guard // to derive this. template <typename Mutex> class unique_lock_guard_base : public detail::lock_guard_base<Mutex, detail::lock_policy_unique> { … }; // unique_lock_guard // // Alias to std::lock_guard. unique_lock_guard; // shared_lock_guard // // A lock-guard which holds shared locks, usable with any shared mutex type. // // Works with both lockable mutex types and lockable-with-state mutex types. template <typename Mutex> class shared_lock_guard : public detail::lock_guard_base<Mutex, detail::lock_policy_shared> { … }; // hybrid_lock_guard // // For shared mutex types, effectively shared_lock_guard; otherwise, // effectively unique_lock_guard. template <typename Mutex> class hybrid_lock_guard : public detail::lock_guard_base<Mutex, detail::lock_policy_hybrid<Mutex>> { … }; template <typename Mutex, typename... A> explicit hybrid_lock_guard(Mutex&, A const&...) -> hybrid_lock_guard<Mutex>; } // namespace folly FOLLY_NAMESPACE_STD_BEGIN template <typename Mutex, typename LockFn = ::folly::access::lock_fn> unique_lock(Mutex&, adopt_lock_t, invoke_result_t<LockFn, Mutex&> const&) -> unique_lock<Mutex>; template <typename Mutex, typename LockFn = ::folly::access::lock_shared_fn> shared_lock(Mutex&, adopt_lock_t, invoke_result_t<LockFn, Mutex&> const&) -> shared_lock<Mutex>; template <typename Mutex, typename LockFn = ::folly::access::lock_upgrade_fn> lock_guard(Mutex&, adopt_lock_t, invoke_result_t<LockFn, Mutex&> const&) -> lock_guard<Mutex>; FOLLY_NAMESPACE_STD_END namespace folly { namespace detail { lock_state_type_of_t_; lock_state_type_of_t; template <typename State> struct transition_lock_result_ { … }; template <> struct transition_lock_result_<void> { … }; transition_lock_result_t_; template <typename From, typename Transition, typename... A> auto transition_lock_2_(From& lock, Transition transition, A const&... a) { … } template <typename From, typename Transition, typename... A> auto transition_lock_1_(From& lock, Transition transition, A const&... a) { … } template <typename To, typename From, typename Transition, typename... A> auto transition_lock_0_(From& lock, Transition transition, A const&... a) { … } template < template <typename> class To, template <typename> class From, typename Mutex, typename Transition, typename... A> auto transition_lock_(From<Mutex>& lock, Transition transition, A const&... a) { … } template <typename, typename> struct transition_lock_policy; transition_lock_policy<unique_lock<Mutex>, shared_lock<Mutex>>; transition_lock_policy<unique_lock<Mutex>, upgrade_lock<Mutex>>; transition_lock_policy<shared_lock<Mutex>, unique_lock<Mutex>>; transition_lock_policy<shared_lock<Mutex>, upgrade_lock<Mutex>>; transition_lock_policy<upgrade_lock<Mutex>, unique_lock<Mutex>>; transition_lock_policy<upgrade_lock<Mutex>, shared_lock<Mutex>>; } // namespace detail // transition_lock // // Represents an atomic transition from the from-lock to the to-lock. Waits // unboundedly for the transition to become available. template < template <typename> class ToLock, typename Mutex, template <typename> class FromLock> ToLock<Mutex> transition_lock(FromLock<Mutex>& lock) { … } // try_transition_lock // // Represents an atomic transition attempt from the from-lock to the to-lock. // Does not wait if the transition is not immediately available. template < template <typename> class ToLock, typename Mutex, template <typename> class FromLock> ToLock<Mutex> try_transition_lock(FromLock<Mutex>& lock) { … } // try_transition_lock_for // // Represents an atomic transition attempt from the from-lock to the to-lock // bounded by a timeout. Waits up to the timeout for the transition to become // available. template < template <typename> class ToLock, typename Mutex, template <typename> class FromLock, typename Rep, typename Period> ToLock<Mutex> try_transition_lock_for( FromLock<Mutex>& lock, std::chrono::duration<Rep, Period> const& timeout) { … } // try_transition_lock_until // // Represents an atomic transition attempt from the from-lock to the to-lock // bounded by a deadline. Waits up to the deadline for the transition to become // available. template < template <typename> class ToLock, typename Mutex, template <typename> class FromLock, typename Clock, typename Duration> ToLock<Mutex> try_transition_lock_until( FromLock<Mutex>& lock, std::chrono::time_point<Clock, Duration> const& deadline) { … } // transition_to_shared_lock(unique_lock) // // Wraps mutex member function unlock_and_lock_shared. // // Represents an immediate atomic downgrade transition from exclusive lock to // to shared lock. template <typename Mutex> shared_lock<Mutex> transition_to_shared_lock(unique_lock<Mutex>& lock) { … } // transition_to_shared_lock(upgrade_lock) // // Wraps mutex member function unlock_upgrade_and_lock_shared. // // Represents an immediate atomic downgrade transition from upgrade lock to // shared lock. template <typename Mutex> shared_lock<Mutex> transition_to_shared_lock(upgrade_lock<Mutex>& lock) { … } // transition_to_upgrade_lock(unique_lock) // // Wraps mutex member function unlock_and_lock_upgrade. // // Represents an immediate atomic downgrade transition from unique lock to // upgrade lock. template <typename Mutex> upgrade_lock<Mutex> transition_to_upgrade_lock(unique_lock<Mutex>& lock) { … } // transition_to_unique_lock(upgrade_lock) // // Wraps mutex member function unlock_upgrade_and_lock. // // Represents an eventual atomic upgrade transition from upgrade lock to unique // lock. template <typename Mutex> unique_lock<Mutex> transition_to_unique_lock(upgrade_lock<Mutex>& lock) { … } // try_transition_to_unique_lock(upgrade_lock) // // Wraps mutex member function try_unlock_upgrade_and_lock. // // Represents an immediate attempted atomic upgrade transition from upgrade // lock to unique lock. template <typename Mutex> unique_lock<Mutex> try_transition_to_unique_lock(upgrade_lock<Mutex>& lock) { … } // try_transition_to_unique_lock_for(upgrade_lock) // // Wraps mutex member function try_unlock_upgrade_and_lock_for. // // Represents an eventual attempted atomic upgrade transition from upgrade // lock to unique lock. template <typename Mutex, typename Rep, typename Period> unique_lock<Mutex> try_transition_to_unique_lock_for( upgrade_lock<Mutex>& lock, std::chrono::duration<Rep, Period> const& timeout) { … } // try_transition_to_unique_lock_until(upgrade_lock) // // Wraps mutex member function try_unlock_upgrade_and_lock_until. // // Represents an eventual attempted atomic upgrade transition from upgrade // lock to unique lock. template <typename Mutex, typename Clock, typename Duration> unique_lock<Mutex> try_transition_to_unique_lock_until( upgrade_lock<Mutex>& lock, std::chrono::time_point<Clock, Duration> const& deadline) { … } // try_transition_to_unique_lock(shared_lock) // // Wraps mutex member function try_unlock_shared_and_lock. // // Represents an immediate attempted atomic upgrade transition from shared // lock to unique lock. template <typename Mutex> unique_lock<Mutex> try_transition_to_unique_lock(shared_lock<Mutex>& lock) { … } // try_transition_to_unique_lock_for(shared_lock) // // Wraps mutex member function try_unlock_shared_and_lock_for. // // Represents an eventual attempted atomic upgrade transition from shared // lock to unique lock. template <typename Mutex, typename Rep, typename Period> unique_lock<Mutex> try_transition_to_unique_lock_for( shared_lock<Mutex>& lock, std::chrono::duration<Rep, Period> const& timeout) { … } // try_transition_to_unique_lock_until(shared_lock) // // Wraps mutex member function try_unlock_shared_and_lock_until. // // Represents an eventual attempted atomic upgrade transition from shared // lock to unique lock. template <typename Mutex, typename Clock, typename Duration> unique_lock<Mutex> try_transition_to_unique_lock_until( shared_lock<Mutex>& lock, std::chrono::time_point<Clock, Duration> const& deadline) { … } // try_transition_to_upgrade_lock(shared_lock) // // Wraps mutex member function try_unlock_shared_and_lock_upgrade. // // Represents an immediate attempted atomic upgrade transition from shared // lock to upgrade lock. template <typename Mutex> upgrade_lock<Mutex> try_transition_to_upgrade_lock(shared_lock<Mutex>& lock) { … } // try_transition_to_upgrade_lock_for(shared_lock) // // Wraps mutex member function try_unlock_shared_and_lock_upgrade_for. // // Represents an eventual attempted atomic upgrade transition from shared // lock to upgrade lock. template <typename Mutex, typename Rep, typename Period> upgrade_lock<Mutex> try_transition_to_upgrade_lock_for( shared_lock<Mutex>& lock, std::chrono::duration<Rep, Period> const& timeout) { … } // try_transition_to_upgrade_lock_until(shared_lock) // // Wraps mutex member function try_unlock_shared_and_lock_upgrade_until. // // Represents an eventual attempted atomic upgrade transition from shared // lock to upgrade lock. template <typename Mutex, typename Clock, typename Duration> upgrade_lock<Mutex> try_transition_to_upgrade_lock_until( shared_lock<Mutex>& lock, std::chrono::time_point<Clock, Duration> const& deadline) { … } } // namespace folly