folly/folly/synchronization/Lock.h

/*
 * 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