libstdc++: Use safe integer comparisons in std::latch [PR98749]
The std::latch::max() function assumes that the returned value can be represented by ptrdiff_t, which is true when __platform_wait_t is int (e.g. on Linux) but not when it's unsigned long, which is the case for most other 64-bit targets. We should use the smaller of PTRDIFF_MAX and std::numeric_limits<__platform_wait_t>::max(). Use std::cmp_less to do a safe comparison that works for all types. We can also use std::cmp_less and std::cmp_equal in std::latch::count_down so that we don't need to deal with comparisons between signed and unsigned. Also add a missing precondition check to constructor and fix the existing check in count_down which was duplicated by mistake. libstdc++-v3/ChangeLog: PR libstdc++/98749 * include/std/latch (latch::max()): Ensure the return value is representable as the return type. (latch::latch(ptrdiff_t)): Add assertion. (latch::count_down): Fix copy & pasted duplicate assertion. Use std::cmp_equal to compare __platform_wait_t and ptrdiff_t values. (latch::_M_a): Use defined constant for alignment. * testsuite/30_threads/latch/1.cc: Check max(). Check constant initialization works for values in the valid range. Check alignment.
This commit is contained in:
parent
bea86e8214
commit
34d8c842b8
2 changed files with 32 additions and 12 deletions
|
@ -41,6 +41,7 @@
|
|||
#ifdef __cpp_lib_latch // C++ >= 20 && atomic_wait
|
||||
#include <bits/atomic_base.h>
|
||||
#include <ext/numeric_traits.h>
|
||||
#include <utility> // cmp_equal, cmp_less_equal, etc.
|
||||
|
||||
namespace std _GLIBCXX_VISIBILITY(default)
|
||||
{
|
||||
|
@ -51,24 +52,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
public:
|
||||
static constexpr ptrdiff_t
|
||||
max() noexcept
|
||||
{ return __gnu_cxx::__int_traits<__detail::__platform_wait_t>::__max; }
|
||||
{
|
||||
using __gnu_cxx::__int_traits;
|
||||
constexpr auto __max = __int_traits<__detail::__platform_wait_t>::__max;
|
||||
if constexpr (std::cmp_less(__max, __PTRDIFF_MAX__))
|
||||
return __max;
|
||||
return __PTRDIFF_MAX__;
|
||||
}
|
||||
|
||||
constexpr explicit latch(ptrdiff_t __expected) noexcept
|
||||
: _M_a(__expected) { }
|
||||
constexpr explicit
|
||||
latch(ptrdiff_t __expected) noexcept
|
||||
: _M_a(__expected)
|
||||
{ __glibcxx_assert(__expected >= 0 && __expected <= max()); }
|
||||
|
||||
~latch() = default;
|
||||
|
||||
latch(const latch&) = delete;
|
||||
latch& operator=(const latch&) = delete;
|
||||
|
||||
_GLIBCXX_ALWAYS_INLINE void
|
||||
count_down(ptrdiff_t __update = 1)
|
||||
{
|
||||
__glibcxx_assert(__update >= 0);
|
||||
auto const __old = __atomic_impl::fetch_sub(&_M_a,
|
||||
__update, memory_order::release);
|
||||
__glibcxx_assert(__update >= 0);
|
||||
if (__old == static_cast<__detail::__platform_wait_t>(__update))
|
||||
__glibcxx_assert(__update >= 0 && __update <= max());
|
||||
auto const __old = __atomic_impl::fetch_sub(&_M_a, __update,
|
||||
memory_order::release);
|
||||
if (std::cmp_equal(__old, __update))
|
||||
__atomic_impl::notify_all(&_M_a);
|
||||
else
|
||||
__glibcxx_assert(std::cmp_less(__update, __old));
|
||||
}
|
||||
|
||||
_GLIBCXX_ALWAYS_INLINE bool
|
||||
|
@ -90,9 +101,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
}
|
||||
|
||||
private:
|
||||
// This alignas is not redundant, it increases the alignment for
|
||||
// long long on x86.
|
||||
alignas(__alignof__(__detail::__platform_wait_t)) __detail::__platform_wait_t _M_a;
|
||||
alignas(__detail::__platform_wait_alignment)
|
||||
__detail::__platform_wait_t _M_a;
|
||||
};
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
} // namespace
|
||||
|
|
|
@ -22,6 +22,16 @@
|
|||
|
||||
#ifndef __cpp_lib_latch
|
||||
# error "Feature-test macro for latch missing in <latch>"
|
||||
#elif __cpp_lib_latch!= 201907L
|
||||
#elif __cpp_lib_latch != 201907L
|
||||
# error "Feature-test macro for latch has wrong value in <latch>"
|
||||
#endif
|
||||
|
||||
static_assert(std::latch::max() > 0);
|
||||
|
||||
constinit std::latch l0(0);
|
||||
constinit std::latch l1(1);
|
||||
constinit std::latch l2(std::latch::max());
|
||||
|
||||
#ifdef _GLIBCXX_RELEASE
|
||||
static_assert(alignof(std::latch) == std::__detail::__platform_wait_alignment);
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue