#include <boost/optional.hpp>
-#include "common/shunique_lock.h"
-
#include "include/ceph_assert.h" // I despise you. Not you the reader, I'm talking
// to the include file.
#ifndef CEPH_COMMON_CONVENIENCE_H
#define CEPH_COMMON_CONVENIENCE_H
-namespace ceph {
-
-// Lock Factories
-// ==============
-//
-// I used to, whenever I declared a mutex member variable of a class,
-// declare a pile of types like:
-// ```cpp
-// using unique_lock = ::std::unique_lock<decltype(membermutex)>;
-// ```
-// to avoid having to type that big, long type at every use. It also
-// let me change the mutex type later. It's inelegant and breaks down
-// if you have more than one type of mutex in the same class. So here
-// are some lock factories.
-template<typename Mutex, typename ...Args>
-inline auto uniquely_lock(Mutex&& m, Args&& ...args)
- -> std::unique_lock<std::remove_reference_t<Mutex> > {
- return std::unique_lock<std::remove_reference_t<Mutex> >(
- std::forward<Mutex>(m), std::forward<Args>(args)... );
-}
-
-template<typename Mutex, typename ...Args>
-inline auto sharingly_lock(Mutex&& m, Args&& ...args)
- -> std::shared_lock<std::remove_reference_t<Mutex> > {
- return
- std::shared_lock<std::remove_reference_t<Mutex> >(
- std::forward<Mutex>(m), std::forward<Args>(args)...);
-}
-
-template<typename Mutex, typename ...Args>
-inline auto shuniquely_lock(std::unique_lock<Mutex>&& m, Args&& ...args)
- -> shunique_lock<std::remove_reference_t<Mutex> > {
- return shunique_lock<std::remove_reference_t<Mutex> >(
- std::forward<std::unique_lock<Mutex> >(m), std::forward<Args>(args)...);
-}
-
-template<typename Mutex, typename ...Args>
-inline auto shuniquely_lock(std::shared_lock<Mutex>&& m, Args&& ...args)
- -> shunique_lock<std::remove_reference_t<Mutex> > {
- return shunique_lock<std::remove_reference_t<Mutex> >(
- std::forward<std::shared_lock<Mutex> >(m),
- std::forward<Args>(args)...);
-}
-
-template<typename Mutex, typename ...Args>
-inline auto shuniquely_lock(Mutex&& m, Args&& ...args)
- -> shunique_lock<std::remove_reference_t<Mutex> > {
- return shunique_lock<std::remove_reference_t<Mutex> >(
- std::forward<Mutex>(m), std::forward<Args>(args)...);
-}
-
-// All right! These two don't work like the others. You cannot do
-// `auto l = guardedly_lock(m)` since copy elision before C++17 is
-// optional. C++17 makes it mandatory so these workarounds won't be
-// needed.
-//
-// To use this, you'll need to do something like:
-// `auto&& l = guardedly_lock(m)`
-// This way, we aren't actually copying or moving a value, we're
-// binding a reference to a temporary which extends its lifetime until
-// the end of the enclosing block.
-//
-// You may in fact want to use
-// `[[gnu::unused]] auto&& l = guardedly_lock(m)`
-// To avoid the unused variable warning. Since reference assignment
-// doesn't have side effects, normally, this just looks to the
-// compiler (whose static analysis is not the sharpest hammer in the
-// drawer) like a reference we bind for no reason and never
-// use. Perhaps future compilers will be smarter about this, but since
-// they'll also implement C++17 people might not care.
-//
-
-template<typename Mutex>
-inline auto guardedly_lock(Mutex&& m)
- -> std::lock_guard<std::remove_reference_t<Mutex> > {
- m.lock();
- // So the way this works is that Copy List Initialization creates
- // one and only one Temporary. There is no implicit copy that is
- // generally optimized away the way there is if we were to just try
- // something like `return std::lock_guard<Mutex>(m)`.
- //
- // The function then returns this temporary as a prvalue. We cannot
- // bind it to a variable, because that would implicitly copy it
- // (even if in practice RVO would mean there is no copy), so instead
- // the user can bind it to a reference. (It has to be either a const
- // lvalue reference or an rvalue reference.)
- //
- // So we get something analogous to all the others with a mildly
- // wonky syntax. The need to use [[gnu::unused]] is honestly the
- // worst part. It makes this construction unfortunately rather
- // long.
- return { std::forward<Mutex>(m), std::adopt_lock };
-}
-
-template<typename Mutex>
-inline auto guardedly_lock(Mutex&& m, std::adopt_lock_t)
- -> std::lock_guard<std::remove_reference_t<Mutex> > {
- return { std::forward<Mutex>(m), std::adopt_lock };
-}
-
-template<typename Mutex, typename Fun, typename...Args>
-inline auto with_unique_lock(Mutex&& mutex, Fun&& fun, Args&&... args)
- -> decltype(fun(std::forward<Args>(args)...)) {
- // Yes I know there's a lock guard inside and not a unique lock, but
- // the caller doesn't need to know or care about the internal
- // details, and the semantics are those of unique locking.
- [[gnu::unused]] auto&& l = guardedly_lock(std::forward<Mutex>(mutex));
- return std::forward<Fun>(fun)(std::forward<Args>(args)...);
-}
-
-template<typename Mutex, typename Fun, typename...Args>
-inline auto with_shared_lock(Mutex&& mutex, Fun&& fun, Args&&... args)
- -> decltype(fun(std::forward<Args>(args)...)) {
- auto l = sharingly_lock(std::forward<Mutex>(mutex));
- return std::forward<Fun>(fun)(std::forward<Args>(args)...);
-}
-}
-
-// Lock Types
-// ----------
-//
-// Lock factories are nice, but you still have to type out a huge,
-// obnoxious template type when declaring a function that takes or
-// returns a lock class.
-//
-#define UNIQUE_LOCK_T(m) \
- ::std::unique_lock<std::remove_reference_t<decltype(m)>>
-#define SHARED_LOCK_T(m) \
- ::std::shared_lock<std::remove_reference_t<decltype(m)>>
-#define SHUNIQUE_LOCK_T(m) \
- ::ceph::shunique_lock<std::remove_reference_t<decltype(m)>>
-
namespace ceph {
// boost::optional is wonderful! Unfortunately it lacks a function for
// the thing you would most obviously want to do with it: apply a