[libc++] Fix native wait alignment (#180928)
This PR fixes two issues regarding the alignment of native wait: - In the internal platform call, the local variable is copied from a potentially non-aligned buffer - Under the unstable ABI, the predicate to test eligibility of a type being able to do native wait is purely on size. We should test also the alignment of such type is qualified for platform call --------- Co-authored-by: Louis Dionne <ldionne.2@gmail.com>
This commit is contained in:
parent
a1c4c1de05
commit
155beb9749
@ -67,8 +67,11 @@ concept __atomic_waitable = requires(const _Tp __t, memory_order __order) {
|
||||
|
||||
# if defined(_LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE)
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr bool __has_native_atomic_wait_impl(size_t __size) {
|
||||
switch (__size) {
|
||||
template <class _Tp>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr bool __has_native_atomic_wait_impl() {
|
||||
if (alignof(_Tp) % sizeof(_Tp) != 0)
|
||||
return false;
|
||||
switch (sizeof(_Tp)) {
|
||||
# define _LIBCPP_MAKE_CASE(n) \
|
||||
case n: \
|
||||
return true;
|
||||
@ -82,7 +85,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __has_native_atomic_wait_impl(size_t __size
|
||||
template <class _Tp>
|
||||
concept __has_native_atomic_wait =
|
||||
has_unique_object_representations_v<_Tp> && is_trivially_copyable_v<_Tp> &&
|
||||
__has_native_atomic_wait_impl(sizeof(_Tp));
|
||||
std::__has_native_atomic_wait_impl<_Tp>();
|
||||
|
||||
# else // _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE
|
||||
|
||||
|
||||
@ -66,7 +66,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
|
||||
template <std::size_t _Size>
|
||||
static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
|
||||
static_assert(_Size == 4, "Can only wait on 4 bytes value");
|
||||
char buffer[_Size];
|
||||
alignas(__cxx_contention_t) char buffer[_Size];
|
||||
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
|
||||
static constexpr timespec __default_timeout = {2, 0};
|
||||
timespec __timeout;
|
||||
@ -99,15 +99,18 @@ extern "C" int __ulock_wake(uint32_t operation, void* addr, uint64_t wake_value)
|
||||
template <std::size_t _Size>
|
||||
static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
|
||||
static_assert(_Size == 8 || _Size == 4, "Can only wait on 8 bytes or 4 bytes value");
|
||||
char buffer[_Size];
|
||||
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
|
||||
auto __timeout_us = __timeout_ns == 0 ? 0 : static_cast<uint32_t>(__timeout_ns / 1000);
|
||||
if constexpr (_Size == 4)
|
||||
if constexpr (_Size == 4) {
|
||||
alignas(uint32_t) char buffer[_Size];
|
||||
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
|
||||
__ulock_wait(
|
||||
UL_COMPARE_AND_WAIT, const_cast<void*>(__ptr), *reinterpret_cast<uint32_t const*>(&buffer), __timeout_us);
|
||||
else
|
||||
} else {
|
||||
alignas(uint64_t) char buffer[_Size];
|
||||
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
|
||||
__ulock_wait(
|
||||
UL_COMPARE_AND_WAIT64, const_cast<void*>(__ptr), *reinterpret_cast<uint64_t const*>(&buffer), __timeout_us);
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t _Size>
|
||||
@ -130,7 +133,7 @@ static void __platform_wake_by_address(void const* __ptr, bool __notify_one) {
|
||||
template <std::size_t _Size>
|
||||
static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
|
||||
static_assert(_Size == 8, "Can only wait on 8 bytes value");
|
||||
char buffer[_Size];
|
||||
alignas(__cxx_contention_t) char buffer[_Size];
|
||||
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
|
||||
if (__timeout_ns == 0) {
|
||||
_umtx_op(const_cast<void*>(__ptr), UMTX_OP_WAIT, *reinterpret_cast<__cxx_contention_t*>(&buffer), nullptr, nullptr);
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: no-threads
|
||||
|
||||
// When __has_native_atomic_wait<T> is true, the atomic object's address will be directly passed to the platform's wait.
|
||||
// This test ensures that types that do not satisfy the platform's wait requirement should have __has_native_atomic_wait<T> be false.
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
template <std::size_t Size, std::size_t Align>
|
||||
struct alignas(Align) Data {
|
||||
char buffer[Size];
|
||||
};
|
||||
|
||||
static_assert(std::__has_native_atomic_wait<std::__cxx_contention_t>);
|
||||
|
||||
#if defined(_LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE) && defined(__APPLE__)
|
||||
|
||||
static_assert(std::__has_native_atomic_wait<Data<4, 4>>);
|
||||
static_assert(std::__has_native_atomic_wait<Data<8, 8>>);
|
||||
|
||||
static_assert(!std::has_unique_object_representations_v<Data<4, 8>>);
|
||||
static_assert(!std::__has_native_atomic_wait<Data<4, 8>>,
|
||||
"Object with !has_unique_object_representations_v should not have native wait");
|
||||
|
||||
static_assert(!std::__has_native_atomic_wait<Data<1, 1>>, "Should only support native wait for 4 and 8 byte types");
|
||||
|
||||
// `__ulock_wait` requires the address is aligned to the requested size (4 or 8)
|
||||
|
||||
static_assert(!std::__has_native_atomic_wait<Data<4, 1>>,
|
||||
"Should only support native wait for types with properly aligned types");
|
||||
|
||||
static_assert(!std::__has_native_atomic_wait<Data<8, 1>>,
|
||||
"Should only support native wait for types with properly aligned types");
|
||||
|
||||
#endif
|
||||
Loading…
x
Reference in New Issue
Block a user