Commit 3fac996e authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

hybrid_lock, either shared_lock or unique_lock

Summary: In generic algorithms parameterized by a mutex type, it may be the case that a shared lock is desired if the mutex is a shared mutex and a unique lock is desired otherwise. This allows a wider range of mutex type arguments rather than just permitting shared mutex types. A hybrid lock may be used with read-only access to data; when the mutex type is shared, the lock would be optimized to a shared lock, but when the mutex type is non-shared, the lock would fall back to a unique lock. This may be helpful since writes may dominate reads in some cases and unique locks on non-shared mutex types may be faster than unique locks on shared mutex types.

Reviewed By: Gownta

Differential Revision: D29624498

fbshipit-source-id: 9caaf9c413fd6ce7ea318906362c3c4330f8d343
parent 046dbf3b
......@@ -339,6 +339,12 @@ struct lock_policy_upgrade {
using unlock_fn = access::unlock_upgrade_fn;
};
template <typename Mutex>
using lock_policy_hybrid = conditional_t<
is_invocable_v<access::lock_shared_fn, Mutex&>,
lock_policy_shared,
lock_policy_unique>;
template <typename Mutex>
using lock_base_unique = lock_base<Mutex, lock_policy_unique>;
......@@ -348,6 +354,9 @@ using lock_base_shared = lock_base<Mutex, lock_policy_shared>;
template <typename Mutex>
using lock_base_upgrade = lock_base<Mutex, lock_policy_upgrade>;
template <typename Mutex>
using lock_base_hybrid = lock_base<Mutex, lock_policy_hybrid<Mutex>>;
} // namespace detail
// unique_lock_base
......@@ -406,7 +415,7 @@ class shared_lock_base : public detail::lock_base_shared<Mutex> {
friend void swap(self& a, self& b) noexcept { a.swap(b); }
};
// upgrade_lock
// upgrade_lock_base
//
// A lock-holder base which holds upgrade locks, usable with any upgrade mutex
// type.
......@@ -435,6 +444,26 @@ class upgrade_lock_base : public detail::lock_base_upgrade<Mutex> {
friend void swap(self& a, self& b) noexcept { a.swap(b); }
};
// 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> {
private:
using base = detail::lock_base_hybrid<Mutex>;
using self = hybrid_lock_base;
public:
using base::base;
void swap(self& that) noexcept { base::swap(that); }
friend void swap(self& a, self& b) noexcept { a.swap(b); }
};
// unique_lock
//
// Alias to std::unique_lock.
......@@ -463,6 +492,23 @@ class upgrade_lock : public upgrade_lock_base<Mutex> {
using upgrade_lock_base<Mutex>::upgrade_lock_base;
};
// 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> {
public:
using hybrid_lock_base<Mutex>::hybrid_lock_base;
};
#if __cpp_deduction_guides >= 201703
template <typename Mutex, typename... A>
explicit hybrid_lock(Mutex&, A const&...) -> hybrid_lock<Mutex>;
#endif
// lock_guard_base
//
// A lock-guard which holds exclusive locks, usable with any mutex type.
......@@ -502,6 +548,26 @@ class shared_lock_guard
using base::base;
};
// 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>> {
private:
using base =
detail::lock_guard_base<Mutex, detail::lock_policy_hybrid<Mutex>>;
public:
using base::base;
};
#if __cpp_deduction_guides >= 201703
template <typename Mutex, typename... A>
explicit hybrid_lock_guard(Mutex&, A const&...) -> hybrid_lock_guard<Mutex>;
#endif
// make_unique_lock
//
// Returns a unique_lock constructed with the given arguments. Deduces the
......@@ -529,6 +595,15 @@ FOLLY_NODISCARD upgrade_lock<Mutex> make_upgrade_lock(Mutex& mutex, A&&... a) {
return upgrade_lock<Mutex>{mutex, static_cast<A&&>(a)...};
}
// make_hybrid_lock
//
// Returns a hybrid_lock constructed with the given arguments. Deduces the
// mutex type.
template <typename Mutex, typename... A>
FOLLY_NODISCARD hybrid_lock<Mutex> make_hybrid_lock(Mutex& mutex, A&&... a) {
return hybrid_lock<Mutex>{mutex, static_cast<A&&>(a)...};
}
namespace detail {
template <typename L>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment