3#include <condition_variable>
10namespace llh::mutexed {
13template<
typename F,
typename A>
15 std::invoke(std::forward<F>(f), a);
23 { m.try_lock_shared() } -> std::same_as<bool>;
34template<
typename Tag,
typename... Ts>
35constexpr bool contains_tag() {
36 return ((std::is_same_v<std::decay_t<Ts>, Tag>) || ...);
40template<
typename Tag,
typename... Ts>
50struct decay_through_ref_wrap {
51 using type = std::decay_t<T>;
55struct decay_through_ref_wrap<std::reference_wrapper<T>> {
56 using type = std::decay_t<T>;
61using decay_through_ref_wrap_t =
typename decay_through_ref_wrap<T>::type;
73 struct lockable_proxy {
76 void lock() { m.mtx_.lock(); }
77 void unlock() { m.mtx_.unlock(); }
78 bool try_lock() {
return m.mtx_.try_lock(); }
80 auto& inner_val_ref() {
return m.val_; }
91 struct lockable_proxy<M const> {
94 explicit lockable_proxy(M
const& c_m) : m(
const_cast<M&
>(c_m)) {}
96 void lock() { m.mtx_.lock_shared(); }
97 void unlock() { m.mtx_.unlock_shared(); }
98 bool try_lock() {
return m.mtx_.try_lock_shared(); }
100 auto const& inner_val_ref() {
return m.val_; }
103 template<
typename M> lockable_proxy(std::reference_wrapper<M>) -> lockable_proxy<M>;
104 template<
typename M> lockable_proxy(M&) -> lockable_proxy<M>;
106 template<
typename F,
typename... M>
107 requires std::conjunction_v<std::is_base_of<mutexed_tag, decay_through_ref_wrap_t<M>>...>
108 decltype(
auto)
operator()(F&& f, M&&... mtxs)
const {
117 return [](
auto&& f,
auto&&... mp) {
118 std::scoped_lock<std::decay_t<
decltype(mp)>...> lock(mp...);
119 return std::invoke(std::forward<F>(f), mp.inner_val_ref()...);
120 }(std::forward<F>(f), lockable_proxy{std::forward<M>(mtxs)}...);
126template<
typename M,
typename H = no_cv>
127struct mutexed_base{};
130struct mutexed_base<M, has_cv> {
131 std::condition_variable_any
mutable cv_;
137struct mutexed_base<std::mutex, has_cv> {
138 std::condition_variable
mutable cv_;
161template<
typename T,
typename M = std::shared_mutex,
typename H = no_cv>
162class Mutexed :
private details::mutexed_tag,
private details::mutexed_base<M, H> {
167 friend details::all_locker;
171 template<
typename DoesNotHaveCV>
172 struct defer_notify {
174 template<
typename Ignored>
175 explicit defer_notify(Ignored&&) {}
179 template<
typename HasCV>
180 requires requires(HasCV& m) { m.cv_.notify_all(); }
181 struct defer_notify<HasCV> {
182 decltype(std::declval<HasCV>().cv_)& cv_;
184 explicit defer_notify(HasCV
const& m) : cv_(m.cv_) {}
191 using notifier = defer_notify<Mutexed>;
213 template<
typename... ValueArgs>
215 std::is_constructible_v<T, ValueArgs&&...>
216 explicit Mutexed(ValueArgs&&... args) : mtx_(), val_(std::forward<ValueArgs>(args)...) {}
220 template<
typename ValArg,
typename MutexArg>
221 explicit Mutexed(ValArg&& v_arg, MutexArg&& m_arg) :
222 mtx_(std::forward<MutexArg>(m_arg)),
223 val_(std::forward<ValArg>(v_arg))
228 template<
typename... MutexArgs>
231 mtx_(std::forward<MutexArgs>(m_args)...),
272 return std::invoke(std::forward<F>(f), val_);
299 std::lock_guard lock(mtx_);
300 return std::invoke(f, val_);
305 template<
typename =
void>
306 requires std::is_copy_constructible_v<T>
363 template<
typename Predicate>
365 void wait(Predicate&& p)
const {
367 this->cv_.wait(lock, [p = std::forward<Predicate>(p),
this](){
return std::invoke(p, val_); });
375 template<
class Rep,
class Period,
typename Predicate>
377 bool wait_for(std::chrono::duration<Rep, Period>
const& rel_time, Predicate&& p)
const {
379 return this->cv_.wait_for(lock, rel_time, [p = std::forward<Predicate>(p),
this](){
return std::invoke(p, val_); });
387 template<
class Clock,
class Duration,
typename Predicate>
389 bool wait_until(std::chrono::time_point<Clock, Duration>
const& timeout_time, Predicate&& p)
const {
391 return this->cv_.wait_until(lock, timeout_time, [p = std::forward<Predicate>(p),
this](){
return std::invoke(p, val_); });
420 void lock() { m.mtx_.lock(); }
421 void unlock() { m.mtx_.unlock(); }
424 explicit Lock(
Mutexed& mtx) : m(mtx) { lock(); }
428 if constexpr (std::is_same_v<H, has_cv>) {
434 Lock(Lock
const&) =
delete;
436 Lock(Lock &&) =
delete;
438 return std::tuple<Lock, T&>(*
this, val_);
441 std::tuple<possibly_shared_lock, T const&>
locked()
const {
464 return std::tuple<possibly_shared_lock, T const&>{mtx_, val_};
470inline constexpr mutex_args_t mutex_args{};
471inline constexpr value_args_t value_args{};
474inline constexpr details::all_locker with_all_locked{};
The Mutexed class is a value-wrapper that protects its value with a mutex that will be referred to in...
std::conditional_t< shared_lockable< M >, std::shared_lock< M >, std::unique_lock< M > > possibly_shared_lock
A std::shared_lock<M> if Mutexed::mutex_type is shared_lockable , a std::unique_lock<M> otherwise.
Mutexed(ValueArgs &&... args)
In-place-constructs the wrapped value with the provided arguments and default-initializes the mutex.
T value_type
The type of the wrapped value.
T get_copy() const
Gets a copy of the wrapped value while locking the inner mutex.
Mutexed(ValArg &&v_arg, MutexArg &&m_arg)
Forwards the first argument to the constructor of the value and the second argument to the constructo...
Mutexed(mutex_args_t, MutexArgs &&... m_args)
In-place-constructs the mutex with the provided arguments and default-initializes the wrapped value.
M mutex_type
The type of the inner mutex
std::tuple< possibly_shared_lock, T const & > locked_const() const
Provides const access to the wrapped value through a tuple of a possibly_shared_lock and a const refe...
decltype(auto) with_locked(F &&f) const
Calls f with a const& or a copy of the wrapped value while locking the inner mutex.
decltype(auto) with_locked(F &&f)
Calls f with a reference on the wrapped value while locking the inner mutex.
std::tuple< possibly_shared_lock, T const & > locked() const
Same as locked_const().
decltype(auto) locked()
Provides access to the wrapped value through a tuple of an unspecified lock guard and a reference to ...
Checks if Tag is not in Ts.
Checks the invokability of F with a value of type A.
Checks if M has the member functions of a shared mutex.
bool wait_until(std::chrono::time_point< Clock, Duration > const &timeout_time, Predicate &&p) const
Waits until this is notified and the provided predicate returns true or until the specified time poin...
bool wait_for(std::chrono::duration< Rep, Period > const &rel_time, Predicate &&p) const
Waits until this is notified and the provided predicate returns true or until the specified duration ...
void wait(Predicate &&p) const
Waits until this is notified and the provided predicate returns true.
A tag type to use as last template argument of Mutexed to enable the waiting API but making it handle...
Disambiguation tag type used to provide arguments for the in-place construction of the inner mutex.
The default last template argument of Mutexed, disabling the waiting API but not pay its costs.
Disambiguation tag type used to provide arguments for the in-place construction of the mutexed value.