#pragma once #include #include #include namespace ivm { namespace detail { // C++17 adds mandatory return value optimization which enables returning a scoped_lock #if __cplusplus >= 201700 && __cpp_lib_scoped_lock template using scoped_lock = std::scoped_lock; #else template using scoped_lock = std::unique_lock; #endif // Detect shared mutex implementation (if any) template struct mutex_traits_t { using mutex_t = std::mutex; static constexpr bool shared = false; }; template <> struct mutex_traits_t { #if __cpp_lib_shared_mutex using mutex_t = std::shared_mutex; static constexpr bool shared = true; #elif __cpp_lib_shared_timed_mutex && !__APPLE__ using mutex_t = std::shared_timed_mutex; static constexpr bool shared = true; #else using mutex_t = std::mutex; static constexpr bool shared = false; #endif }; // Detect lock needed for mutex + wait template struct lock_traits_t; template struct lock_traits_t { using read_t = typename std::conditional, scoped_lock>::type; using write_t = scoped_lock; }; template struct lock_traits_t { static_assert(Traits::waitable, "Mutex is not waitable"); using read_t = typename std::conditional, std::unique_lock>::type; using write_t = std::unique_lock; }; // std::condition_variable is only good for std::unique_lock template struct condition_variable_t { using type_t = std::condition_variable_any; }; template <> struct condition_variable_t { using type_t = std::condition_variable; }; // Holds the lock and provides pointer semantics template class lock_holder_t { template friend struct wait_impl_t; public: explicit lock_holder_t(Lockable& lockable) : lockable{lockable}, lock{lockable.mutex} {} auto operator*() -> auto& { return lockable.resource; } auto operator*() const -> auto& { return lockable.resource; } auto operator->() { return &lockable.resource; } auto operator->() const { return &lockable.resource; } private: Lockable& lockable; Lock lock; }; // `wait` implementation template struct wait_impl_t; template struct wait_impl_t { using lock_t = Type; }; template struct wait_impl_t { class lock_t : public Type { public: using Type::Type; void wait() { wait_impl(*this); } }; private: template static void wait_impl(Lock& lock) { lock.lockable.cv.wait(lock.lock); } }; // Internal `condition_variable` storage template class condition_variable_holder_t {}; template class condition_variable_holder_t { template friend struct wait_impl_t; public: void notify_one() { cv.notify_one(); } void notify_all() { cv.notify_all(); } private: mutable typename condition_variable_t::type_t cv; }; // Holds resource and mutex template class lockable_impl_t : public condition_variable_holder_t { template friend class lock_holder_t; public: lockable_impl_t() = default; template explicit lockable_impl_t(Args&&... args) : resource{std::forward(args)...} {} lockable_impl_t(const lockable_impl_t&) = delete; ~lockable_impl_t() = default; auto operator=(const lockable_impl_t&) = delete; template auto read() const { return typename wait_impl_t::read_t>>::lock_t{*this}; } template auto write() { return typename wait_impl_t::write_t>>::lock_t{*this}; } private: Type resource{}; mutable typename Traits::mutex_t mutex; }; // Combine mutex traits and waitable traits template struct waitable_traits_t { static constexpr bool waitable = Waitable; }; template struct lockable_traits_t : mutex_traits_t, waitable_traits_t {}; } // namespace detail template using lockable_t = detail::lockable_impl_t>; } // namespace ivm