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
──────────────────────────────────────────────────────────────────────────