On Monday, 21 June 2021 19:31:59 CEST Jonathan Wakely via Gcc-patches wrote: > diff --git a/libstdc++-v3/include/std/mutex b/libstdc++-v3/include/std/mutex > index d4c5d13f654..5f2d8f9ee7b 100644 > --- a/libstdc++-v3/include/std/mutex > +++ b/libstdc++-v3/include/std/mutex > @@ -512,47 +512,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #endif // _GLIBCXX_HAS_GTHREADS > > /// @cond undocumented > - template<typename _Lock> > - inline unique_lock<_Lock> > - __try_to_lock(_Lock& __l) > - { return unique_lock<_Lock>{__l, try_to_lock}; } > + namespace __detail > + { > + template<typename _Lockable> > + inline unique_lock<_Lockable> > + __try_to_lock(_Lockable& __l) > + { return unique_lock<_Lockable>{__l, try_to_lock}; } > > - template<int _Idx, bool _Continue = true> > - struct __try_lock_impl > - { > - template<typename... _Lock> > - static void > - __do_try_lock(tuple<_Lock&...>& __locks, int& __idx) > - { > - __idx = _Idx; > - auto __lock = std::__try_to_lock(std::get<_Idx>(__locks)); > - if (__lock.owns_lock()) > - { > - constexpr bool __cont = _Idx + 2 < sizeof...(_Lock); > - using __try_locker = __try_lock_impl<_Idx + 1, __cont>; > - __try_locker::__do_try_lock(__locks, __idx); > - if (__idx == -1) > - __lock.release(); > - } > - } > - }; > + // Lock the last element of the tuple, after all previous ones are > locked. + template<int _Idx, typename... _Lockables> > + inline __enable_if_t<_Idx + 1 == sizeof...(_Lockables), int> > + __try_lock_impl(tuple<_Lockables&...>& __lockables)
Couldn't you drop the need for enable_if and tuple if you define the function like this? (Or - without constexpr if - two overloads with __try_lock_impl(_L1& __l1) and __try_lock_impl(_L1& __l1, _L2& __l2, _Lockables&... __lockables) template<typename _L1, typename... _Lockables> inline int __try_lock_impl(_L1& __l1, _Lockables&... __lockables) { if (auto __lock = __detail::__try_to_lock(__l1)) { if constexpr (sizeof...(_Lockables)) { int __idx = __detail::__try_lock_impl(__lockables...); if (__idx >= 0) return __idx + 1; } __lock.release(); return -1; } else return 0; } > [...] > + template<typename _L0, typename... _L1> > + void > + __lock_impl(int& __i, int __depth, _L0& __l0, _L1&... __l1) > + { How about optimizing a likely common case where all lockables have the same type? In that case we don't require recursion and can manage stack usage much simpler: if constexpr ((is_same_v<_L0, _L1> && ...)) { constexpr int _Np = 1 + sizeof...(_L1); std::array<unique_lock<_L0>, _Np> __locks = { {__l0, defer_lock}, {__l1, defer_lock}... }; int __first = 0; do { __locks[__first].lock(); for (int __j = 1; __j < _Np; ++__j) { const int __idx = (__first + __j) % _Np; if (!__locks[__idx].try_lock()) { for (int __k = __idx; __k != __first; __k = __k == 1 ? _Np : __k - 1) __locks[__k - 1].unlock(); __first = __idx; break; } } } while (!__locks[__first]); for (int __j = 0; __j < _Np; ++__j) __locks[__j].release(); } else > + while (__i >= __depth) > + { > + if (__i == __depth) > + { > + int __failed = 1; // index that couldn't be locked > + { > + unique_lock<_L0> __first(__l0); > + auto __rest = std::tie(__l1...); > + __failed += __detail::__try_lock_impl<0>(__rest); > + if (!__failed) > + { > + __i = -1; // finished > + __first.release(); > + return; > + } > + } > +#ifdef _GLIBCXX_USE_SCHED_YIELD > + __gthread_yield(); > +#endif > + constexpr auto __n = 1 + sizeof...(_L1); > + __i = (__depth + __failed) % __n; > + } > + else // rotate left until l_i is first. > + __detail::__lock_impl(__i, __depth + 1, __l1..., __l0); > + } > + } > + > + } // namespace __detail > + /// @endcond > + > /** @brief Generic lock. > * @param __l1 Meets Lockable requirements (try_lock() may throw). > * @param __l2 Meets Lockable requirements (try_lock() may throw). > @@ -590,19 +627,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > void > lock(_L1& __l1, _L2& __l2, _L3&... __l3) > { > - while (true) > - { > - using __try_locker = __try_lock_impl<0, sizeof...(_L3) != 0>; > - unique_lock<_L1> __first(__l1); > - int __idx; > - auto __locks = std::tie(__l2, __l3...); > - __try_locker::__do_try_lock(__locks, __idx); > - if (__idx == -1) > - { > - __first.release(); > - return; > - } > - } > + int __i = 0; > + __detail::__lock_impl(__i, 0, __l1, __l2, __l3...); > } > > #if __cplusplus >= 201703L -- ────────────────────────────────────────────────────────────────────────── Dr. Matthias Kretz https://mattkretz.github.io GSI Helmholtz Centre for Heavy Ion Research https://gsi.de std::experimental::simd https://github.com/VcDevel/std-simd ──────────────────────────────────────────────────────────────────────────