31 #ifndef ETCPAL_CPP_SYNCHRONIZED_VALUE_H_
32 #define ETCPAL_CPP_SYNCHRONIZED_VALUE_H_
36 #include <type_traits>
83 template <
typename T,
typename LockType = Mutex>
84 class SynchronizedValue;
85 template <
typename T,
typename LockType = Mutex>
86 class ConstUniqueLockPtr;
87 template <
typename T,
typename LockType = Mutex>
93 template <
typename...>
98 template <
typename First,
typename... Rest>
129 template <
typename LockType>
152 template <
typename T,
typename LockType = Mutex>
160 if (!sync_->lock_.Lock())
161 ETCPAL_THROW(std::runtime_error(
"etcpal SynchronizedValue lock failed."));
172 ETCPAL_THROW(std::runtime_error(
"etcpal ConstStrictLockPtr constructed from a non-owning handle."));
173 other.sync_ =
nullptr;
188 sync_->lock_.Unlock();
192 const T&
operator*() const noexcept {
return sync_->value_; }
194 const T*
operator->() const noexcept {
return std::addressof(sync_->value_); }
210 template <
typename T,
typename LockType = Mutex>
235 return const_cast<T&
>(this->sync_->value_);
240 return const_cast<T*
>(std::addressof(this->sync_->value_));
254 template <
typename T,
typename LockType>
264 : sync_(&sync), owns_(
true)
270 : sync_(&sync), owns_(
false)
277 : sync_(&sync), owns_(sync.TryLockFor(timeout_ms))
288 other.sync_ =
nullptr;
296 explicit operator bool() const noexcept {
return owns_; }
305 sync_->lock_.Unlock();
315 ETCPAL_THROW(std::runtime_error(
"etcpal lock handle does not reference a lock."));
317 ETCPAL_THROW(std::runtime_error(
"etcpal lock handle already owns the lock."));
318 if (!sync_->lock_.Lock())
319 ETCPAL_THROW(std::runtime_error(
"etcpal SynchronizedValue lock failed."));
335 ETCPAL_THROW(std::runtime_error(
"etcpal lock handle does not reference a lock."));
337 ETCPAL_THROW(std::runtime_error(
"etcpal lock handle already owns the lock."));
338 owns_ = sync_->TryLockFor(timeout_ms);
354 ETCPAL_THROW(std::runtime_error(
"etcpal lock handle does not own the lock."));
355 return sync_->value_;
362 ETCPAL_THROW(std::runtime_error(
"etcpal lock handle does not own the lock."));
363 return std::addressof(sync_->value_);
381 template <
typename T,
typename LockType>
408 using ConstUniqueLockPtr<T, LockType>::operator*;
409 using ConstUniqueLockPtr<T, LockType>::operator->;
417 ETCPAL_THROW(std::runtime_error(
"etcpal lock handle does not own the lock."));
418 return const_cast<T&
>(this->sync_->value_);
425 ETCPAL_THROW(std::runtime_error(
"etcpal lock handle does not own the lock."));
426 return const_cast<T*
>(std::addressof(this->sync_->value_));
441 template <
typename T,
typename LockType>
451 template <
typename... Args,
452 typename =
typename std::enable_if<
453 std::is_constructible<T, Args&&...>::value &&
454 !std::is_same<
typename std::decay<
typename detail::FirstType<Args...>::type>::type,
458 explicit SynchronizedValue(Args&&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
459 : value_(std::forward<Args>(args)...)
466 T& GetValueUnsafe() noexcept {
return value_; }
467 LockType& GetLockUnsafe() noexcept {
return lock_; }
504 ConstUniqueLockPtr<T, LockType> TryToSynchronize(
int timeout_ms = 0) const noexcept
506 return ConstUniqueLockPtr<T, LockType>(*
this, TryToLockTag{}, timeout_ms);
544 template <
typename F>
545 decltype(
auto) Apply(F&& func)
547 static_assert(!std::is_reference<decltype(std::forward<F>(func)(std::declval<T&>()))>::value,
548 "Apply must not return a reference; it would outlive the lock");
550 return std::forward<F>(func)(*lock);
552 template <
typename F>
553 decltype(
auto) Apply(F&& func)
const
555 static_assert(!std::is_reference<decltype(std::forward<F>(func)(std::declval<const T&>()))>::value,
556 "Apply must not return a reference; it would outlive the lock");
558 return std::forward<F>(func)(*lock);
563 friend class ConstStrictLockPtr<T, LockType>;
564 friend class StrictLockPtr<T, LockType>;
565 friend class ConstUniqueLockPtr<T, LockType>;
566 friend class UniqueLockPtr<T, LockType>;
570 bool TryLockFor(
int timeout_ms)
const noexcept
572 return SupportsTimedLock<LockType>::value ? lock_.TryLock(timeout_ms) : lock_.TryLock(0);
576 mutable LockType lock_;
An RAII handle holding a SynchronizedValue's lock and granting read-only access to the value.
Definition: synchronized_value.h:154
ConstStrictLockPtr(const SynchronizedValue< T, LockType > &sync, AdoptLockTag) noexcept
Adopt the given SynchronizedValue's lock, which the calling thread must already hold.
Definition: synchronized_value.h:165
ConstStrictLockPtr(ConstStrictLockPtr &&other) noexcept
Move ownership of the held lock from another handle, which is left empty.
Definition: synchronized_value.h:182
const T * operator->() const noexcept
Access a member of the guarded value. Undefined behavior if this handle has been moved from.
Definition: synchronized_value.h:194
~ConstStrictLockPtr() noexcept
Release the lock.
Definition: synchronized_value.h:185
ConstStrictLockPtr(ConstUniqueLockPtr< T, LockType > &&other)
Take over the lock held by other, which is left empty.
Definition: synchronized_value.h:169
const T & operator*() const noexcept
Get a reference to the guarded value. Undefined behavior if this handle has been moved from.
Definition: synchronized_value.h:192
ConstStrictLockPtr(const SynchronizedValue< T, LockType > &sync)
Lock the given SynchronizedValue for read-only access.
Definition: synchronized_value.h:158
An RAII handle for read-only access whose lock may or may not be held.
Definition: synchronized_value.h:256
void Release() noexcept
Disassociate from the lock without unlocking it; the caller becomes responsible for the lock.
Definition: synchronized_value.h:343
ConstUniqueLockPtr(const SynchronizedValue< T, LockType > &sync, DeferLockTag) noexcept
Reference the given SynchronizedValue without acquiring the lock.
Definition: synchronized_value.h:269
const T & operator*() const
Get a reference to the guarded value.
Definition: synchronized_value.h:351
const T * operator->() const
Access a member of the guarded value.
Definition: synchronized_value.h:359
void Unlock() noexcept
Release the lock early, if owned. Does nothing otherwise.
Definition: synchronized_value.h:301
void Lock()
Acquire the lock, blocking until it is available.
Definition: synchronized_value.h:312
bool TryLock(int timeout_ms=0)
Try to acquire the lock, waiting up to timeout_ms.
Definition: synchronized_value.h:332
ConstUniqueLockPtr(ConstUniqueLockPtr &&other) noexcept
Move ownership from another handle, which is left empty.
Definition: synchronized_value.h:286
ConstUniqueLockPtr(const SynchronizedValue< T, LockType > &sync, AdoptLockTag) noexcept
Adopt the given SynchronizedValue's lock, which the calling thread must already hold.
Definition: synchronized_value.h:263
ConstUniqueLockPtr(const SynchronizedValue< T, LockType > &sync)
Lock the given SynchronizedValue for read-only access, blocking until the lock is acquired.
Definition: synchronized_value.h:260
ConstUniqueLockPtr(const SynchronizedValue< T, LockType > &sync, TryToLockTag, int timeout_ms=0) noexcept
Try to lock the given SynchronizedValue, waiting up to timeout_ms; check OwnsLock() for the result.
Definition: synchronized_value.h:276
bool OwnsLock() const noexcept
Whether this handle currently owns the lock.
Definition: synchronized_value.h:298
~ConstUniqueLockPtr() noexcept
Release the lock if owned.
Definition: synchronized_value.h:293
A wrapper class for the EtcPal mutex type.
Definition: mutex.h:88
A wrapper class for the EtcPal recursive mutex type.
Definition: recursive_mutex.h:64
An RAII handle holding a SynchronizedValue's lock and granting mutable access to the value.
Definition: synchronized_value.h:212
StrictLockPtr(UniqueLockPtr< T, LockType > &&other)
Take over the lock held by other, which is left empty.
Definition: synchronized_value.h:226
T & operator*() noexcept
Get a mutable reference to the guarded value. Undefined behavior if this handle has been moved from.
Definition: synchronized_value.h:233
StrictLockPtr(SynchronizedValue< T, LockType > &sync, AdoptLockTag) noexcept
Adopt the given SynchronizedValue's lock, which the calling thread must already hold.
Definition: synchronized_value.h:219
StrictLockPtr(SynchronizedValue< T, LockType > &sync)
Lock the given SynchronizedValue for mutable access.
Definition: synchronized_value.h:216
T * operator->() noexcept
Access a member of the guarded value. Undefined behavior if this handle has been moved from.
Definition: synchronized_value.h:238
A value bundled with a lock that guards all access to it.
Definition: synchronized_value.h:443
UniqueLockPtr< T, LockType > DeferSynchronize() noexcept
Reference the value without locking; acquire later with Lock() or TryLock() on the handle.
Definition: synchronized_value.h:521
UniqueLockPtr< T, LockType > UniqueSynchronize()
Lock the value, returning a handle that can also Unlock(), re-Lock(), or Release() it.
Definition: synchronized_value.h:517
StrictLockPtr< T, LockType > operator->()
Lock the value and access a member of it for the duration of the enclosing expression.
Definition: synchronized_value.h:476
ConstStrictLockPtr< T, LockType > Synchronize() const
Lock the value, returning a handle that holds the lock until it is destroyed.
Definition: synchronized_value.h:486
UniqueLockPtr< T, LockType > AdoptSynchronize() noexcept
Adopt the lock, which the calling thread must already hold, into a handle that will release it.
Definition: synchronized_value.h:528
ConstStrictLockPtr< T, LockType > operator->() const
Lock the value and access a member of it for the duration of the enclosing expression.
Definition: synchronized_value.h:479
StrictLockPtr< T, LockType > Synchronize()
Lock the value, returning a handle that holds the lock until it is destroyed.
Definition: synchronized_value.h:483
SynchronizedValue(Args &&... args) noexcept(std::is_nothrow_constructible< T, Args... >::value)
Construct the guarded value in place, forwarding the given arguments to its constructor.
Definition: synchronized_value.h:458
An RAII handle for mutable access whose lock may or may not be held.
Definition: synchronized_value.h:383
UniqueLockPtr(SynchronizedValue< T, LockType > &sync, TryToLockTag, int timeout_ms=0) noexcept
Try to lock the given SynchronizedValue, waiting up to timeout_ms; check OwnsLock() for the result.
Definition: synchronized_value.h:403
UniqueLockPtr(SynchronizedValue< T, LockType > &sync)
Lock the given SynchronizedValue for mutable access, blocking until the lock is acquired.
Definition: synchronized_value.h:387
UniqueLockPtr(SynchronizedValue< T, LockType > &sync, DeferLockTag) noexcept
Reference the given SynchronizedValue without acquiring the lock.
Definition: synchronized_value.h:396
UniqueLockPtr(SynchronizedValue< T, LockType > &sync, AdoptLockTag) noexcept
Adopt the given SynchronizedValue's lock, which the calling thread must already hold.
Definition: synchronized_value.h:390
T * operator->()
Access a member of the guarded value.
Definition: synchronized_value.h:422
T & operator*()
Get a mutable reference to the guarded value.
Definition: synchronized_value.h:414
Common definitions used by EtcPal C++ wrappers.
C++ wrapper and utilities for etcpal/mutex.h.
C++ wrapper and utilities for etcpal/recursive_mutex.h.
Tag requesting that a lock already held by the calling thread be adopted rather than acquired.
Definition: synchronized_value.h:108
Tag requesting that the lock be left unacquired on construction, to be acquired later with ConstUniqu...
Definition: synchronized_value.h:114
Trait reporting whether SynchronizedValue::TryToSynchronize() honors its timeout for LockType on the ...
Definition: synchronized_value.h:131
Tag requesting that the lock be acquired with a poll or optional timed wait, never failing with an ex...
Definition: synchronized_value.h:120
Meta struct to extract the first type from a parameter pack.
Definition: synchronized_value.h:95