// (C) Copyright 2010 Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk // (C) Copyright 2012 Vicente J. Botet Escriba // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_THREAD_SYNCHRONIZED_VALUE_HPP #define BOOST_THREAD_SYNCHRONIZED_VALUE_HPP #include #include #include #include #include #include #include #include #include #include //#include //#include //#if ! defined BOOST_NO_CXX11_HDR_TYPE_TRAITS //#include //#endif #if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) #include // todo change to once Boost.Tuple or Boost.Fusion provides Move semantics on C++98 compilers. #include #endif #include #include namespace boost { /** * strict lock providing a const pointer access to the synchronized value type. * * @param T the value type. * @param Lockable the mutex type protecting the value type. */ template class const_strict_lock_ptr { public: typedef T value_type; typedef Lockable mutex_type; protected: // this should be a strict_lock, but unique_lock is needed to be able to return it. boost::unique_lock lk_; T const& value_; public: BOOST_THREAD_MOVABLE_ONLY( const_strict_lock_ptr ) /** * @param value constant reference of the value to protect. * @param mtx reference to the mutex used to protect the value. * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. */ const_strict_lock_ptr(T const& val, Lockable & mtx) : lk_(mtx), value_(val) { } const_strict_lock_ptr(T const& val, Lockable & mtx, adopt_lock_t tag) BOOST_NOEXCEPT : lk_(mtx, tag), value_(val) { } /** * Move constructor. * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. */ const_strict_lock_ptr(BOOST_THREAD_RV_REF(const_strict_lock_ptr) other) BOOST_NOEXCEPT : lk_(boost::move(BOOST_THREAD_RV(other).lk_)),value_(BOOST_THREAD_RV(other).value_) { } ~const_strict_lock_ptr() { } /** * @return a constant pointer to the protected value */ const T* operator->() const { return &value_; } /** * @return a constant reference to the protected value */ const T& operator*() const { return value_; } }; /** * strict lock providing a pointer access to the synchronized value type. * * @param T the value type. * @param Lockable the mutex type protecting the value type. */ template class strict_lock_ptr : public const_strict_lock_ptr { typedef const_strict_lock_ptr base_type; public: BOOST_THREAD_MOVABLE_ONLY( strict_lock_ptr ) /** * @param value reference of the value to protect. * @param mtx reference to the mutex used to protect the value. * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. */ strict_lock_ptr(T & val, Lockable & mtx) : base_type(val, mtx) { } strict_lock_ptr(T & val, Lockable & mtx, adopt_lock_t tag) : base_type(val, mtx, tag) { } /** * Move constructor. * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. */ strict_lock_ptr(BOOST_THREAD_RV_REF(strict_lock_ptr) other) : base_type(boost::move(static_cast(other))) { } ~strict_lock_ptr() { } /** * @return a pointer to the protected value */ T* operator->() { return const_cast(&this->value_); } /** * @return a reference to the protected value */ T& operator*() { return const_cast(this->value_); } }; template struct synchronized_value_strict_lock_ptr { typedef strict_lock_ptr type; }; template struct synchronized_value_strict_lock_ptr { typedef const_strict_lock_ptr type; }; /** * unique_lock providing a const pointer access to the synchronized value type. * * An object of type const_unique_lock_ptr is a unique_lock that provides a const pointer access to the synchronized value type. * As unique_lock controls the ownership of a lockable object within a scope. * Ownership of the lockable object may be acquired at construction or after construction, * and may be transferred, after acquisition, to another const_unique_lock_ptr object. * Objects of type const_unique_lock_ptr are not copyable but are movable. * The behavior of a program is undefined if the mutex and the value type * pointed do not exist for the entire remaining lifetime of the const_unique_lock_ptr object. * The supplied Mutex type shall meet the BasicLockable requirements. * * @note const_unique_lock_ptr meets the Lockable requirements. * If Lockable meets the TimedLockable requirements, const_unique_lock_ptr * also meets the TimedLockable requirements. * * @param T the value type. * @param Lockable the mutex type protecting the value type. */ template class const_unique_lock_ptr : public unique_lock { typedef unique_lock base_type; public: typedef T value_type; typedef Lockable mutex_type; protected: T const& value_; public: BOOST_THREAD_MOVABLE_ONLY(const_unique_lock_ptr) /** * @param value reference of the value to protect. * @param mtx reference to the mutex used to protect the value. * * @requires If mutex_type is not a recursive mutex the calling thread does not own the mutex. * * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. */ const_unique_lock_ptr(T const& val, Lockable & mtx) : base_type(mtx), value_(val) { } /** * @param value reference of the value to protect. * @param mtx reference to the mutex used to protect the value. * @param tag of type adopt_lock_t used to differentiate the constructor. * @requires The calling thread own the mutex. * @effects stores a reference to it and to the value type @c value taking ownership. */ const_unique_lock_ptr(T const& val, Lockable & mtx, adopt_lock_t) BOOST_NOEXCEPT : base_type(mtx, adopt_lock), value_(val) { } /** * @param value reference of the value to protect. * @param mtx reference to the mutex used to protect the value. * @param tag of type defer_lock_t used to differentiate the constructor. * @effects stores a reference to it and to the value type @c value c. */ const_unique_lock_ptr(T const& val, Lockable & mtx, defer_lock_t) BOOST_NOEXCEPT : base_type(mtx, defer_lock), value_(val) { } /** * @param value reference of the value to protect. * @param mtx reference to the mutex used to protect the value. * @param tag of type try_to_lock_t used to differentiate the constructor. * @requires If mutex_type is not a recursive mutex the calling thread does not own the mutex. * @effects try to lock the mutex @c mtx, stores a reference to it and to the value type @c value. */ const_unique_lock_ptr(T const& val, Lockable & mtx, try_to_lock_t) BOOST_NOEXCEPT : base_type(mtx, try_to_lock), value_(val) { } /** * Move constructor. * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. */ const_unique_lock_ptr(BOOST_THREAD_RV_REF(const_unique_lock_ptr) other) BOOST_NOEXCEPT : base_type(boost::move(static_cast(other))), value_(BOOST_THREAD_RV(other).value_) { } /** * @effects If owns calls unlock() on the owned mutex. */ ~const_unique_lock_ptr() { } /** * @return a constant pointer to the protected value */ const T* operator->() const { BOOST_ASSERT (this->owns_lock()); return &value_; } /** * @return a constant reference to the protected value */ const T& operator*() const { BOOST_ASSERT (this->owns_lock()); return value_; } }; /** * unique lock providing a pointer access to the synchronized value type. * * @param T the value type. * @param Lockable the mutex type protecting the value type. */ template class unique_lock_ptr : public const_unique_lock_ptr { typedef const_unique_lock_ptr base_type; public: typedef T value_type; typedef Lockable mutex_type; BOOST_THREAD_MOVABLE_ONLY(unique_lock_ptr) /** * @param value reference of the value to protect. * @param mtx reference to the mutex used to protect the value. * @effects locks the mutex @c mtx, stores a reference to it and to the value type @c value. */ unique_lock_ptr(T & val, Lockable & mtx) : base_type(val, mtx) { } /** * @param value reference of the value to protect. * @param mtx reference to the mutex used to protect the value. * @param tag of type adopt_lock_t used to differentiate the constructor. * @effects stores a reference to it and to the value type @c value taking ownership. */ unique_lock_ptr(T & value, Lockable & mtx, adopt_lock_t) BOOST_NOEXCEPT : base_type(value, mtx, adopt_lock) { } /** * @param value reference of the value to protect. * @param mtx reference to the mutex used to protect the value. * @param tag of type defer_lock_t used to differentiate the constructor. * @effects stores a reference to it and to the value type @c value c. */ unique_lock_ptr(T & value, Lockable & mtx, defer_lock_t) BOOST_NOEXCEPT : base_type(value, mtx, defer_lock) { } /** * @param value reference of the value to protect. * @param mtx reference to the mutex used to protect the value. * @param tag of type try_to_lock_t used to differentiate the constructor. * @effects try to lock the mutex @c mtx, stores a reference to it and to the value type @c value. */ unique_lock_ptr(T & value, Lockable & mtx, try_to_lock_t) BOOST_NOEXCEPT : base_type(value, mtx, try_to_lock) { } /** * Move constructor. * @effects takes ownership of the mutex owned by @c other, stores a reference to the mutex and the value type of @c other. */ unique_lock_ptr(BOOST_THREAD_RV_REF(unique_lock_ptr) other) BOOST_NOEXCEPT : base_type(boost::move(static_cast(other))) { } ~unique_lock_ptr() { } /** * @return a pointer to the protected value */ T* operator->() { BOOST_ASSERT (this->owns_lock()); return const_cast(&this->value_); } /** * @return a reference to the protected value */ T& operator*() { BOOST_ASSERT (this->owns_lock()); return const_cast(this->value_); } }; template struct synchronized_value_unique_lock_ptr { typedef unique_lock_ptr type; }; template struct synchronized_value_unique_lock_ptr { typedef const_unique_lock_ptr type; }; /** * cloaks a value type and the mutex used to protect it together. * @param T the value type. * @param Lockable the mutex type protecting the value type. */ template class synchronized_value { #if ! defined(BOOST_THREAD_NO_MAKE_UNIQUE_LOCKS) #if ! defined BOOST_NO_CXX11_VARIADIC_TEMPLATES template friend std::tuple::type ...> synchronize(SV& ...sv); #else template friend std::tuple< typename synchronized_value_strict_lock_ptr::type, typename synchronized_value_strict_lock_ptr::type > synchronize(SV1& sv1, SV2& sv2); template friend std::tuple< typename synchronized_value_strict_lock_ptr::type, typename synchronized_value_strict_lock_ptr::type, typename synchronized_value_strict_lock_ptr::type > synchronize(SV1& sv1, SV2& sv2, SV3& sv3); #endif #endif public: typedef T value_type; typedef Lockable mutex_type; private: T value_; mutable mutex_type mtx_; public: // construction/destruction /** * Default constructor. * * @Requires: T is DefaultConstructible */ synchronized_value() //BOOST_NOEXCEPT_IF(is_nothrow_default_constructible::value) : value_() { } /** * Constructor from copy constructible value. * * Requires: T is CopyConstructible */ synchronized_value(T const& other) //BOOST_NOEXCEPT_IF(is_nothrow_copy_constructible::value) : value_(other) { } /** * Move Constructor. * * Requires: T is CopyMovable */ synchronized_value(BOOST_THREAD_RV_REF(T) other) //BOOST_NOEXCEPT_IF(is_nothrow_move_constructible::value) : value_(boost::move(other)) { } /** * Constructor from value type. * * Requires: T is DefaultConstructible and Assignable * Effects: Assigns the value on a scope protected by the mutex of the rhs. The mutex is not copied. */ synchronized_value(synchronized_value const& rhs) { strict_lock lk(rhs.mtx_); value_ = rhs.value_; } /** * Move Constructor from movable value type * */ synchronized_value(BOOST_THREAD_RV_REF(synchronized_value) other) { strict_lock lk(BOOST_THREAD_RV(other).mtx_); value_= boost::move(BOOST_THREAD_RV(other).value_); } // mutation /** * Assignment operator. * * Effects: Copies the underlying value on a scope protected by the two mutexes. * The mutex is not copied. The locks are acquired using lock, so deadlock is avoided. * For example, there is no problem if one thread assigns a = b and the other assigns b = a. * * Return: *this */ synchronized_value& operator=(synchronized_value const& rhs) { if(&rhs != this) { // auto _ = make_unique_locks(mtx_, rhs.mtx_); unique_lock lk1(mtx_, defer_lock); unique_lock lk2(rhs.mtx_, defer_lock); lock(lk1,lk2); value_ = rhs.value_; } return *this; } /** * Assignment operator from a T const&. * Effects: The operator copies the value on a scope protected by the mutex. * Return: *this */ synchronized_value& operator=(value_type const& val) { { strict_lock lk(mtx_); value_ = val; } return *this; } //observers /** * Explicit conversion to value type. * * Requires: T is CopyConstructible * Return: A copy of the protected value obtained on a scope protected by the mutex. * */ T get() const { strict_lock lk(mtx_); return value_; } /** * Explicit conversion to value type. * * Requires: T is CopyConstructible * Return: A copy of the protected value obtained on a scope protected by the mutex. * */ #if ! defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) explicit operator T() const { return get(); } #endif /** * value type getter. * * Return: A constant reference to the protected value. * * Note: Not thread safe * */ T const& value() const { return value_; } /** * mutex getter. * * Return: A constant reference to the protecting mutex. * * Note: Not thread safe * */ mutex_type const& mutex() const { return mtx_; } /** * Swap * * Effects: Swaps the data. Again, locks are acquired using lock(). The mutexes are not swapped. * A swap method accepts a T& and swaps the data inside a critical section. * This is by far the preferred method of changing the guarded datum wholesale because it keeps the lock only * for a short time, thus lowering the pressure on the mutex. */ void swap(synchronized_value & rhs) { if (this == &rhs) { return; } // auto _ = make_unique_locks(mtx_, rhs.mtx_); unique_lock lk1(mtx_, defer_lock); unique_lock lk2(rhs.mtx_, defer_lock); lock(lk1,lk2); boost::swap(value_, rhs.value_); } /** * Swap with the underlying value type * * Effects: Swaps the data on a scope protected by the mutex. */ void swap(value_type & rhs) { strict_lock lk(mtx_); boost::swap(value_, rhs); } /** * Essentially calling a method obj->foo(x, y, z) calls the method foo(x, y, z) inside a critical section as * long-lived as the call itself. */ strict_lock_ptr operator->() { return BOOST_THREAD_MAKE_RV_REF((strict_lock_ptr(value_, mtx_))); } /** * If the synchronized_value object involved is const-qualified, then you'll only be able to call const methods * through operator->. So, for example, vec->push_back("xyz") won't work if vec were const-qualified. * The locking mechanism capitalizes on the assumption that const methods don't modify their underlying data. */ const_strict_lock_ptr operator->() const { return BOOST_THREAD_MAKE_RV_REF((const_strict_lock_ptr(value_, mtx_))); } /** * Call function on a locked block. * * @requires fct(value_) is well formed. * * Example * void fun(synchronized_value> & v) { * v ( [](vector> & vec) * { * vec.push_back(42); * assert(vec.back() == 42); * } ); * } */ template inline typename boost::result_of::type operator()(BOOST_THREAD_RV_REF(F) fct) { strict_lock lk(mtx_); return fct(value_); } template inline typename boost::result_of::type operator()(BOOST_THREAD_RV_REF(F) fct) const { strict_lock lk(mtx_); return fct(value_); } #if defined BOOST_NO_CXX11_RVALUE_REFERENCES template inline typename boost::result_of::type operator()(F const & fct) { strict_lock lk(mtx_); return fct(value_); } template inline typename boost::result_of::type operator()(F const & fct) const { strict_lock lk(mtx_); return fct(value_); } template inline R operator()(R(*fct)(value_type&)) { strict_lock lk(mtx_); return fct(value_); } template inline R operator()(R(*fct)(value_type const&)) const { strict_lock lk(mtx_); return fct(value_); } #endif /** * The synchronize() factory make easier to lock on a scope. * As discussed, operator-> can only lock over the duration of a call, so it is insufficient for complex operations. * With synchronize() you get to lock the object in a scoped and to directly access the object inside that scope. * * Example * void fun(synchronized_value> & v) { * auto&& vec=v.synchronize(); * vec.push_back(42); * assert(vec.back() == 42); * } */ strict_lock_ptr synchronize() { return BOOST_THREAD_MAKE_RV_REF((strict_lock_ptr(value_, mtx_))); } const_strict_lock_ptr synchronize() const { return BOOST_THREAD_MAKE_RV_REF((const_strict_lock_ptr(value_, mtx_))); } unique_lock_ptr unique_synchronize() { return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_))); } const_unique_lock_ptr unique_synchronize() const { return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr(value_, mtx_))); } unique_lock_ptr unique_synchronize(defer_lock_t tag) { return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_, tag))); } const_unique_lock_ptr unique_synchronize(defer_lock_t tag) const { return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr(value_, mtx_, tag))); } unique_lock_ptr defer_synchronize() BOOST_NOEXCEPT { return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_, defer_lock))); } const_unique_lock_ptr defer_synchronize() const BOOST_NOEXCEPT { return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr(value_, mtx_, defer_lock))); } unique_lock_ptr try_to_synchronize() BOOST_NOEXCEPT { return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_, try_to_lock))); } const_unique_lock_ptr try_to_synchronize() const BOOST_NOEXCEPT { return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr(value_, mtx_, try_to_lock))); } unique_lock_ptr adopt_synchronize() BOOST_NOEXCEPT { return BOOST_THREAD_MAKE_RV_REF((unique_lock_ptr(value_, mtx_, adopt_lock))); } const_unique_lock_ptr adopt_synchronize() const BOOST_NOEXCEPT { return BOOST_THREAD_MAKE_RV_REF((const_unique_lock_ptr(value_, mtx_, adopt_lock))); } #if ! defined __IBMCPP__ private: #endif class deref_value { private: friend class synchronized_value; boost::unique_lock lk_; T& value_; explicit deref_value(synchronized_value& outer): lk_(outer.mtx_),value_(outer.value_) {} public: BOOST_THREAD_MOVABLE_ONLY(deref_value) deref_value(BOOST_THREAD_RV_REF(deref_value) other): lk_(boost::move(BOOST_THREAD_RV(other).lk_)),value_(BOOST_THREAD_RV(other).value_) {} operator T&() { return value_; } deref_value& operator=(T const& newVal) { value_=newVal; return *this; } }; class const_deref_value { private: friend class synchronized_value; boost::unique_lock lk_; const T& value_; explicit const_deref_value(synchronized_value const& outer): lk_(outer.mtx_), value_(outer.value_) {} public: BOOST_THREAD_MOVABLE_ONLY(const_deref_value) const_deref_value(BOOST_THREAD_RV_REF(const_deref_value) other): lk_(boost::move(BOOST_THREAD_RV(other).lk_)), value_(BOOST_THREAD_RV(other).value_) {} operator const T&() { return value_; } }; public: deref_value operator*() { return BOOST_THREAD_MAKE_RV_REF(deref_value(*this)); } const_deref_value operator*() const { return BOOST_THREAD_MAKE_RV_REF(const_deref_value(*this)); } // io functions /** * @requires T is OutputStreamable * @effects saves the value type on the output stream @c os. */ template void save(OStream& os) const { strict_lock lk(mtx_); os << value_; } /** * @requires T is InputStreamable * @effects loads the value type from the input stream @c is. */ template void load(IStream& is) { strict_lock lk(mtx_); is >> value_; } // relational operators /** * @requires T is EqualityComparable * */ bool operator==(synchronized_value const& rhs) const { unique_lock lk1(mtx_, defer_lock); unique_lock lk2(rhs.mtx_, defer_lock); lock(lk1,lk2); return value_ == rhs.value_; } /** * @requires T is LessThanComparable * */ bool operator<(synchronized_value const& rhs) const { unique_lock lk1(mtx_, defer_lock); unique_lock lk2(rhs.mtx_, defer_lock); lock(lk1,lk2); return value_ < rhs.value_; } /** * @requires T is GreaterThanComparable * */ bool operator>(synchronized_value const& rhs) const { unique_lock lk1(mtx_, defer_lock); unique_lock lk2(rhs.mtx_, defer_lock); lock(lk1,lk2); return value_ > rhs.value_; } bool operator<=(synchronized_value const& rhs) const { unique_lock lk1(mtx_, defer_lock); unique_lock lk2(rhs.mtx_, defer_lock); lock(lk1,lk2); return value_ <= rhs.value_; } bool operator>=(synchronized_value const& rhs) const { unique_lock lk1(mtx_, defer_lock); unique_lock lk2(rhs.mtx_, defer_lock); lock(lk1,lk2); return value_ >= rhs.value_; } bool operator==(value_type const& rhs) const { unique_lock lk1(mtx_); return value_ == rhs; } bool operator!=(value_type const& rhs) const { unique_lock lk1(mtx_); return value_ != rhs; } bool operator<(value_type const& rhs) const { unique_lock lk1(mtx_); return value_ < rhs; } bool operator<=(value_type const& rhs) const { unique_lock lk1(mtx_); return value_ <= rhs; } bool operator>(value_type const& rhs) const { unique_lock lk1(mtx_); return value_ > rhs; } bool operator>=(value_type const& rhs) const { unique_lock lk1(mtx_); return value_ >= rhs; } }; // Specialized algorithms /** * */ template inline void swap(synchronized_value & lhs, synchronized_value & rhs) { lhs.swap(rhs); } template inline void swap(synchronized_value & lhs, T & rhs) { lhs.swap(rhs); } template inline void swap(T & lhs, synchronized_value & rhs) { rhs.swap(lhs); } //Hash support // template struct hash; // template // struct hash >; // Comparison with T template bool operator!=(synchronized_value const&lhs, synchronized_value const& rhs) { return ! (lhs==rhs); } template bool operator==(T const& lhs, synchronized_value const&rhs) { return rhs==lhs; } template bool operator!=(T const& lhs, synchronized_value const&rhs) { return rhs!=lhs; } template bool operator<(T const& lhs, synchronized_value const&rhs) { return rhs>lhs; } template bool operator<=(T const& lhs, synchronized_value const&rhs) { return rhs>=lhs; } template bool operator>(T const& lhs, synchronized_value const&rhs) { return rhs bool operator>=(T const& lhs, synchronized_value const&rhs) { return rhs<=lhs; } /** * */ template inline OStream& operator<<(OStream& os, synchronized_value const& rhs) { rhs.save(os); return os; } template inline IStream& operator>>(IStream& is, synchronized_value& rhs) { rhs.load(is); return is; } #if ! defined(BOOST_THREAD_NO_SYNCHRONIZE) #if ! defined BOOST_NO_CXX11_VARIADIC_TEMPLATES template std::tuple::type ...> synchronize(SV& ...sv) { boost::lock(sv.mtx_ ...); typedef std::tuple::type ...> t_type; return t_type(typename synchronized_value_strict_lock_ptr::type(sv.value_, sv.mtx_, adopt_lock) ...); } #else template std::tuple< typename synchronized_value_strict_lock_ptr::type, typename synchronized_value_strict_lock_ptr::type > synchronize(SV1& sv1, SV2& sv2) { boost::lock(sv1.mtx_, sv2.mtx_); typedef std::tuple< typename synchronized_value_strict_lock_ptr::type, typename synchronized_value_strict_lock_ptr::type > t_type; return t_type( typename synchronized_value_strict_lock_ptr::type(sv1.value_, sv1.mtx_, adopt_lock), typename synchronized_value_strict_lock_ptr::type(sv2.value_, sv2.mtx_, adopt_lock) ); } template std::tuple< typename synchronized_value_strict_lock_ptr::type, typename synchronized_value_strict_lock_ptr::type, typename synchronized_value_strict_lock_ptr::type > synchronize(SV1& sv1, SV2& sv2, SV3& sv3) { boost::lock(sv1.mtx_, sv2.mtx_); typedef std::tuple< typename synchronized_value_strict_lock_ptr::type, typename synchronized_value_strict_lock_ptr::type, typename synchronized_value_strict_lock_ptr::type > t_type; return t_type( typename synchronized_value_strict_lock_ptr::type(sv1.value_, sv1.mtx_, adopt_lock), typename synchronized_value_strict_lock_ptr::type(sv2.value_, sv2.mtx_, adopt_lock), typename synchronized_value_strict_lock_ptr::type(sv3.value_, sv3.mtx_, adopt_lock) ); } #endif #endif } #include #endif // header