Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ee57019

Browse files
huixie90ldionne
authored andcommitted
[libc++] Fix native wait alignment (llvm#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 <[email protected]> (cherry picked from commit 155beb9)
1 parent 51df921 commit ee57019

3 files changed

Lines changed: 60 additions & 9 deletions

File tree

libcxx/include/__atomic/atomic_waitable_traits.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,11 @@ concept __atomic_waitable = requires(const _Tp __t, memory_order __order) {
6969

7070
# if defined(_LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE)
7171

72-
_LIBCPP_HIDE_FROM_ABI constexpr bool __has_native_atomic_wait_impl(size_t __size) {
73-
switch (__size) {
72+
template <class _Tp>
73+
_LIBCPP_HIDE_FROM_ABI constexpr bool __has_native_atomic_wait_impl() {
74+
if (alignof(_Tp) % sizeof(_Tp) != 0)
75+
return false;
76+
switch (sizeof(_Tp)) {
7477
# define _LIBCPP_MAKE_CASE(n) \
7578
case n: \
7679
return true;
@@ -84,7 +87,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __has_native_atomic_wait_impl(size_t __size
8487
template <class _Tp>
8588
concept __has_native_atomic_wait =
8689
has_unique_object_representations_v<_Tp> && is_trivially_copyable_v<_Tp> &&
87-
__has_native_atomic_wait_impl(sizeof(_Tp));
90+
std::__has_native_atomic_wait_impl<_Tp>();
8891

8992
# else // _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE
9093

libcxx/src/atomic.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
6666
template <std::size_t _Size>
6767
static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
6868
static_assert(_Size == 4, "Can only wait on 4 bytes value");
69-
char buffer[_Size];
69+
alignas(__cxx_contention_t) char buffer[_Size];
7070
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
7171
static constexpr timespec __default_timeout = {2, 0};
7272
timespec __timeout;
@@ -99,15 +99,18 @@ extern "C" int __ulock_wake(uint32_t operation, void* addr, uint64_t wake_value)
9999
template <std::size_t _Size>
100100
static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
101101
static_assert(_Size == 8 || _Size == 4, "Can only wait on 8 bytes or 4 bytes value");
102-
char buffer[_Size];
103-
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
104102
auto __timeout_us = __timeout_ns == 0 ? 0 : static_cast<uint32_t>(__timeout_ns / 1000);
105-
if constexpr (_Size == 4)
103+
if constexpr (_Size == 4) {
104+
alignas(uint32_t) char buffer[_Size];
105+
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
106106
__ulock_wait(
107107
UL_COMPARE_AND_WAIT, const_cast<void*>(__ptr), *reinterpret_cast<uint32_t const*>(&buffer), __timeout_us);
108-
else
108+
} else {
109+
alignas(uint64_t) char buffer[_Size];
110+
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
109111
__ulock_wait(
110112
UL_COMPARE_AND_WAIT64, const_cast<void*>(__ptr), *reinterpret_cast<uint64_t const*>(&buffer), __timeout_us);
113+
}
111114
}
112115

113116
template <std::size_t _Size>
@@ -130,7 +133,7 @@ static void __platform_wake_by_address(void const* __ptr, bool __notify_one) {
130133
template <std::size_t _Size>
131134
static void __platform_wait_on_address(void const* __ptr, void const* __val, uint64_t __timeout_ns) {
132135
static_assert(_Size == 8, "Can only wait on 8 bytes value");
133-
char buffer[_Size];
136+
alignas(__cxx_contention_t) char buffer[_Size];
134137
std::memcpy(&buffer, const_cast<const void*>(__val), _Size);
135138
if (__timeout_ns == 0) {
136139
_umtx_op(const_cast<void*>(__ptr), UMTX_OP_WAIT, *reinterpret_cast<__cxx_contention_t*>(&buffer), nullptr, nullptr);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
// UNSUPPORTED: no-threads
11+
12+
// When __has_native_atomic_wait<T> is true, the atomic object's address will be directly passed to the platform's wait.
13+
// This test ensures that types that do not satisfy the platform's wait requirement should have __has_native_atomic_wait<T> be false.
14+
15+
#include <atomic>
16+
#include <cstddef>
17+
#include <type_traits>
18+
19+
template <std::size_t Size, std::size_t Align>
20+
struct alignas(Align) Data {
21+
char buffer[Size];
22+
};
23+
24+
static_assert(std::__has_native_atomic_wait<std::__cxx_contention_t>);
25+
26+
#if defined(_LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE) && defined(__APPLE__)
27+
28+
static_assert(std::__has_native_atomic_wait<Data<4, 4>>);
29+
static_assert(std::__has_native_atomic_wait<Data<8, 8>>);
30+
31+
static_assert(!std::has_unique_object_representations_v<Data<4, 8>>);
32+
static_assert(!std::__has_native_atomic_wait<Data<4, 8>>,
33+
"Object with !has_unique_object_representations_v should not have native wait");
34+
35+
static_assert(!std::__has_native_atomic_wait<Data<1, 1>>, "Should only support native wait for 4 and 8 byte types");
36+
37+
// `__ulock_wait` requires the address is aligned to the requested size (4 or 8)
38+
39+
static_assert(!std::__has_native_atomic_wait<Data<4, 1>>,
40+
"Should only support native wait for types with properly aligned types");
41+
42+
static_assert(!std::__has_native_atomic_wait<Data<8, 1>>,
43+
"Should only support native wait for types with properly aligned types");
44+
45+
#endif

0 commit comments

Comments
 (0)