// -*- C++ -*- //===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef _LIBCPP___MUTEX_BASE #define _LIBCPP___MUTEX_BASE #include <__chrono/duration.h> #include <__chrono/steady_clock.h> #include <__chrono/system_clock.h> #include <__chrono/time_point.h> #include <__config> #include <__threading_support> #include #include #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header #endif _LIBCPP_PUSH_MACROS #include <__undef_macros> _LIBCPP_BEGIN_NAMESPACE_STD #ifndef _LIBCPP_HAS_NO_THREADS class _LIBCPP_TYPE_VIS _LIBCPP_THREAD_SAFETY_ANNOTATION(capability("mutex")) mutex { __libcpp_mutex_t __m_ = _LIBCPP_MUTEX_INITIALIZER; public: _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR mutex() = default; mutex(const mutex&) = delete; mutex& operator=(const mutex&) = delete; #if defined(_LIBCPP_HAS_TRIVIAL_MUTEX_DESTRUCTION) ~mutex() = default; #else ~mutex() _NOEXCEPT; #endif void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability()); bool try_lock() _NOEXCEPT _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_capability(true)); void unlock() _NOEXCEPT _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability()); typedef __libcpp_mutex_t* native_handle_type; _LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() {return &__m_;} }; static_assert(is_nothrow_default_constructible::value, "the default constructor for std::mutex must be nothrow"); struct _LIBCPP_TYPE_VIS defer_lock_t { explicit defer_lock_t() = default; }; struct _LIBCPP_TYPE_VIS try_to_lock_t { explicit try_to_lock_t() = default; }; struct _LIBCPP_TYPE_VIS adopt_lock_t { explicit adopt_lock_t() = default; }; #if defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_BUILDING_LIBRARY) extern _LIBCPP_EXPORTED_FROM_ABI const defer_lock_t defer_lock; extern _LIBCPP_EXPORTED_FROM_ABI const try_to_lock_t try_to_lock; extern _LIBCPP_EXPORTED_FROM_ABI const adopt_lock_t adopt_lock; #else /* inline */ constexpr defer_lock_t defer_lock = defer_lock_t(); /* inline */ constexpr try_to_lock_t try_to_lock = try_to_lock_t(); /* inline */ constexpr adopt_lock_t adopt_lock = adopt_lock_t(); #endif template class _LIBCPP_TEMPLATE_VIS _LIBCPP_THREAD_SAFETY_ANNOTATION(scoped_lockable) lock_guard { public: typedef _Mutex mutex_type; private: mutex_type& __m_; public: _LIBCPP_NODISCARD_EXT _LIBCPP_INLINE_VISIBILITY explicit lock_guard(mutex_type& __m) _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability(__m)) : __m_(__m) {__m_.lock();} _LIBCPP_NODISCARD_EXT _LIBCPP_INLINE_VISIBILITY lock_guard(mutex_type& __m, adopt_lock_t) _LIBCPP_THREAD_SAFETY_ANNOTATION(requires_capability(__m)) : __m_(__m) {} _LIBCPP_INLINE_VISIBILITY ~lock_guard() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability()) {__m_.unlock();} private: lock_guard(lock_guard const&) = delete; lock_guard& operator=(lock_guard const&) = delete; }; _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(lock_guard); template class _LIBCPP_TEMPLATE_VIS unique_lock { public: typedef _Mutex mutex_type; private: mutex_type* __m_; bool __owns_; public: _LIBCPP_INLINE_VISIBILITY unique_lock() _NOEXCEPT : __m_(nullptr), __owns_(false) {} _LIBCPP_INLINE_VISIBILITY explicit unique_lock(mutex_type& __m) : __m_(_VSTD::addressof(__m)), __owns_(true) {__m_->lock();} _LIBCPP_INLINE_VISIBILITY unique_lock(mutex_type& __m, defer_lock_t) _NOEXCEPT : __m_(_VSTD::addressof(__m)), __owns_(false) {} _LIBCPP_INLINE_VISIBILITY unique_lock(mutex_type& __m, try_to_lock_t) : __m_(_VSTD::addressof(__m)), __owns_(__m.try_lock()) {} _LIBCPP_INLINE_VISIBILITY unique_lock(mutex_type& __m, adopt_lock_t) : __m_(_VSTD::addressof(__m)), __owns_(true) {} template _LIBCPP_INLINE_VISIBILITY unique_lock(mutex_type& __m, const chrono::time_point<_Clock, _Duration>& __t) : __m_(_VSTD::addressof(__m)), __owns_(__m.try_lock_until(__t)) {} template _LIBCPP_INLINE_VISIBILITY unique_lock(mutex_type& __m, const chrono::duration<_Rep, _Period>& __d) : __m_(_VSTD::addressof(__m)), __owns_(__m.try_lock_for(__d)) {} _LIBCPP_INLINE_VISIBILITY ~unique_lock() { if (__owns_) __m_->unlock(); } unique_lock(unique_lock const&) = delete; unique_lock& operator=(unique_lock const&) = delete; _LIBCPP_INLINE_VISIBILITY unique_lock(unique_lock&& __u) _NOEXCEPT : __m_(__u.__m_), __owns_(__u.__owns_) {__u.__m_ = nullptr; __u.__owns_ = false;} _LIBCPP_INLINE_VISIBILITY unique_lock& operator=(unique_lock&& __u) _NOEXCEPT { if (__owns_) __m_->unlock(); __m_ = __u.__m_; __owns_ = __u.__owns_; __u.__m_ = nullptr; __u.__owns_ = false; return *this; } void lock(); bool try_lock(); template bool try_lock_for(const chrono::duration<_Rep, _Period>& __d); template bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __t); void unlock(); _LIBCPP_INLINE_VISIBILITY void swap(unique_lock& __u) _NOEXCEPT { _VSTD::swap(__m_, __u.__m_); _VSTD::swap(__owns_, __u.__owns_); } _LIBCPP_INLINE_VISIBILITY mutex_type* release() _NOEXCEPT { mutex_type* __m = __m_; __m_ = nullptr; __owns_ = false; return __m; } _LIBCPP_INLINE_VISIBILITY bool owns_lock() const _NOEXCEPT {return __owns_;} _LIBCPP_INLINE_VISIBILITY explicit operator bool() const _NOEXCEPT {return __owns_;} _LIBCPP_INLINE_VISIBILITY mutex_type* mutex() const _NOEXCEPT {return __m_;} }; _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(unique_lock); template void unique_lock<_Mutex>::lock() { if (__m_ == nullptr) __throw_system_error(EPERM, "unique_lock::lock: references null mutex"); if (__owns_) __throw_system_error(EDEADLK, "unique_lock::lock: already locked"); __m_->lock(); __owns_ = true; } template bool unique_lock<_Mutex>::try_lock() { if (__m_ == nullptr) __throw_system_error(EPERM, "unique_lock::try_lock: references null mutex"); if (__owns_) __throw_system_error(EDEADLK, "unique_lock::try_lock: already locked"); __owns_ = __m_->try_lock(); return __owns_; } template template bool unique_lock<_Mutex>::try_lock_for(const chrono::duration<_Rep, _Period>& __d) { if (__m_ == nullptr) __throw_system_error(EPERM, "unique_lock::try_lock_for: references null mutex"); if (__owns_) __throw_system_error(EDEADLK, "unique_lock::try_lock_for: already locked"); __owns_ = __m_->try_lock_for(__d); return __owns_; } template template bool unique_lock<_Mutex>::try_lock_until(const chrono::time_point<_Clock, _Duration>& __t) { if (__m_ == nullptr) __throw_system_error(EPERM, "unique_lock::try_lock_until: references null mutex"); if (__owns_) __throw_system_error(EDEADLK, "unique_lock::try_lock_until: already locked"); __owns_ = __m_->try_lock_until(__t); return __owns_; } template void unique_lock<_Mutex>::unlock() { if (!__owns_) __throw_system_error(EPERM, "unique_lock::unlock: not locked"); __m_->unlock(); __owns_ = false; } template inline _LIBCPP_INLINE_VISIBILITY void swap(unique_lock<_Mutex>& __x, unique_lock<_Mutex>& __y) _NOEXCEPT {__x.swap(__y);} //enum class cv_status _LIBCPP_DECLARE_STRONG_ENUM(cv_status) { no_timeout, timeout }; _LIBCPP_DECLARE_STRONG_ENUM_EPILOG(cv_status) class _LIBCPP_TYPE_VIS condition_variable { __libcpp_condvar_t __cv_ = _LIBCPP_CONDVAR_INITIALIZER; public: _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR condition_variable() _NOEXCEPT = default; #ifdef _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION ~condition_variable() = default; #else ~condition_variable(); #endif condition_variable(const condition_variable&) = delete; condition_variable& operator=(const condition_variable&) = delete; void notify_one() _NOEXCEPT; void notify_all() _NOEXCEPT; void wait(unique_lock& __lk) _NOEXCEPT; template _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS void wait(unique_lock& __lk, _Predicate __pred); template _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS cv_status wait_until(unique_lock& __lk, const chrono::time_point<_Clock, _Duration>& __t); template _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS bool wait_until(unique_lock& __lk, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred); template _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS cv_status wait_for(unique_lock& __lk, const chrono::duration<_Rep, _Period>& __d); template bool _LIBCPP_INLINE_VISIBILITY wait_for(unique_lock& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred); typedef __libcpp_condvar_t* native_handle_type; _LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() {return &__cv_;} private: void __do_timed_wait(unique_lock& __lk, chrono::time_point) _NOEXCEPT; #if defined(_LIBCPP_HAS_COND_CLOCKWAIT) void __do_timed_wait(unique_lock& __lk, chrono::time_point) _NOEXCEPT; #endif template void __do_timed_wait(unique_lock& __lk, chrono::time_point<_Clock, chrono::nanoseconds>) _NOEXCEPT; }; #endif // !_LIBCPP_HAS_NO_THREADS template inline _LIBCPP_INLINE_VISIBILITY __enable_if_t::value, chrono::nanoseconds> __safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) { using namespace chrono; using __ratio = ratio_divide<_Period, nano>; using __ns_rep = nanoseconds::rep; _Rep __result_float = __d.count() * __ratio::num / __ratio::den; _Rep __result_max = numeric_limits<__ns_rep>::max(); if (__result_float >= __result_max) { return nanoseconds::max(); } _Rep __result_min = numeric_limits<__ns_rep>::min(); if (__result_float <= __result_min) { return nanoseconds::min(); } return nanoseconds(static_cast<__ns_rep>(__result_float)); } template inline _LIBCPP_INLINE_VISIBILITY __enable_if_t::value, chrono::nanoseconds> __safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) { using namespace chrono; if (__d.count() == 0) { return nanoseconds(0); } using __ratio = ratio_divide<_Period, nano>; using __ns_rep = nanoseconds::rep; __ns_rep __result_max = numeric_limits<__ns_rep>::max(); if (__d.count() > 0 && __d.count() > __result_max / __ratio::num) { return nanoseconds::max(); } __ns_rep __result_min = numeric_limits<__ns_rep>::min(); if (__d.count() < 0 && __d.count() < __result_min / __ratio::num) { return nanoseconds::min(); } __ns_rep __result = __d.count() * __ratio::num / __ratio::den; if (__result == 0) { return nanoseconds(1); } return nanoseconds(__result); } #ifndef _LIBCPP_HAS_NO_THREADS template void condition_variable::wait(unique_lock& __lk, _Predicate __pred) { while (!__pred()) wait(__lk); } template cv_status condition_variable::wait_until(unique_lock& __lk, const chrono::time_point<_Clock, _Duration>& __t) { using namespace chrono; using __clock_tp_ns = time_point<_Clock, nanoseconds>; typename _Clock::time_point __now = _Clock::now(); if (__t <= __now) return cv_status::timeout; __clock_tp_ns __t_ns = __clock_tp_ns(_VSTD::__safe_nanosecond_cast(__t.time_since_epoch())); __do_timed_wait(__lk, __t_ns); return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout; } template bool condition_variable::wait_until(unique_lock& __lk, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred) { while (!__pred()) { if (wait_until(__lk, __t) == cv_status::timeout) return __pred(); } return true; } template cv_status condition_variable::wait_for(unique_lock& __lk, const chrono::duration<_Rep, _Period>& __d) { using namespace chrono; if (__d <= __d.zero()) return cv_status::timeout; using __ns_rep = nanoseconds::rep; steady_clock::time_point __c_now = steady_clock::now(); #if defined(_LIBCPP_HAS_COND_CLOCKWAIT) using __clock_tp_ns = time_point; __ns_rep __now_count_ns = _VSTD::__safe_nanosecond_cast(__c_now.time_since_epoch()).count(); #else using __clock_tp_ns = time_point; __ns_rep __now_count_ns = _VSTD::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count(); #endif __ns_rep __d_ns_count = _VSTD::__safe_nanosecond_cast(__d).count(); if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) { __do_timed_wait(__lk, __clock_tp_ns::max()); } else { __do_timed_wait(__lk, __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count))); } return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout; } template inline bool condition_variable::wait_for(unique_lock& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred) { return wait_until(__lk, chrono::steady_clock::now() + __d, _VSTD::move(__pred)); } #if defined(_LIBCPP_HAS_COND_CLOCKWAIT) inline void condition_variable::__do_timed_wait(unique_lock& __lk, chrono::time_point __tp) _NOEXCEPT { using namespace chrono; if (!__lk.owns_lock()) __throw_system_error(EPERM, "condition_variable::timed wait: mutex not locked"); nanoseconds __d = __tp.time_since_epoch(); timespec __ts; seconds __s = duration_cast(__d); using __ts_sec = decltype(__ts.tv_sec); const __ts_sec __ts_sec_max = numeric_limits<__ts_sec>::max(); if (__s.count() < __ts_sec_max) { __ts.tv_sec = static_cast<__ts_sec>(__s.count()); __ts.tv_nsec = (__d - __s).count(); } else { __ts.tv_sec = __ts_sec_max; __ts.tv_nsec = giga::num - 1; } int __ec = pthread_cond_clockwait(&__cv_, __lk.mutex()->native_handle(), CLOCK_MONOTONIC, &__ts); if (__ec != 0 && __ec != ETIMEDOUT) __throw_system_error(__ec, "condition_variable timed_wait failed"); } #endif // _LIBCPP_HAS_COND_CLOCKWAIT template inline void condition_variable::__do_timed_wait(unique_lock& __lk, chrono::time_point<_Clock, chrono::nanoseconds> __tp) _NOEXCEPT { wait_for(__lk, __tp - _Clock::now()); } #endif // !_LIBCPP_HAS_NO_THREADS _LIBCPP_END_NAMESPACE_STD _LIBCPP_POP_MACROS #endif // _LIBCPP___MUTEX_BASE