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



Reply via email to