Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ set(files
__atomic/atomic_lock_free.h
__atomic/atomic_ref.h
__atomic/atomic_sync.h
__atomic/atomic_waitable_traits.h
__atomic/check_memory_order.h
__atomic/contention_t.h
__atomic/fence.h
Expand Down
6 changes: 5 additions & 1 deletion libcxx/include/__atomic/atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define _LIBCPP___ATOMIC_ATOMIC_H

#include <__atomic/atomic_sync.h>
#include <__atomic/atomic_waitable_traits.h>
#include <__atomic/check_memory_order.h>
#include <__atomic/floating_point_helper.h>
#include <__atomic/is_always_lock_free.h>
Expand Down Expand Up @@ -200,6 +201,7 @@ struct __atomic_base<_Tp, true> : public __atomic_base<_Tp, false> {
_LIBCPP_HIDE_FROM_ABI _Tp operator^=(_Tp __op) _NOEXCEPT { return fetch_xor(__op) ^ __op; }
};

#if _LIBCPP_STD_VER >= 20
// Here we need _IsIntegral because the default template argument is not enough
// e.g __atomic_base<int> is __atomic_base<int, true>, which inherits from
// __atomic_base<int, false> and the caller of the wait function is
Expand Down Expand Up @@ -228,6 +230,8 @@ struct __atomic_waitable_traits<__atomic_base<_Tp, _IsIntegral> > {
}
};

#endif // _LIBCPP_STD_VER >= 20

template <typename _Tp>
struct __check_atomic_mandates {
using type _LIBCPP_NODEBUG = _Tp;
Expand Down Expand Up @@ -321,10 +325,10 @@ struct atomic<_Tp*> : public __atomic_base<_Tp*> {
atomic& operator=(const atomic&) volatile = delete;
};

#if _LIBCPP_STD_VER >= 20
template <class _Tp>
struct __atomic_waitable_traits<atomic<_Tp> > : __atomic_waitable_traits<__atomic_base<_Tp> > {};

#if _LIBCPP_STD_VER >= 20
template <class _Tp>
requires is_floating_point_v<_Tp>
struct atomic<_Tp> : __atomic_base<_Tp> {
Expand Down
3 changes: 3 additions & 0 deletions libcxx/include/__atomic/atomic_flag.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define _LIBCPP___ATOMIC_ATOMIC_FLAG_H

#include <__atomic/atomic_sync.h>
#include <__atomic/atomic_waitable_traits.h>
#include <__atomic/contention_t.h>
#include <__atomic/memory_order.h>
#include <__atomic/support.h>
Expand Down Expand Up @@ -74,6 +75,7 @@ struct atomic_flag {
atomic_flag& operator=(const atomic_flag&) volatile = delete;
};

#if _LIBCPP_STD_VER >= 20
template <>
struct __atomic_waitable_traits<atomic_flag> {
using __value_type _LIBCPP_NODEBUG = _LIBCPP_ATOMIC_FLAG_TYPE;
Expand All @@ -97,6 +99,7 @@ struct __atomic_waitable_traits<atomic_flag> {
return std::addressof(__a.__a_);
}
};
#endif // _LIBCPP_STD_VER >= 20

inline _LIBCPP_HIDE_FROM_ABI bool atomic_flag_test(const volatile atomic_flag* __o) _NOEXCEPT { return __o->test(); }

Expand Down
1 change: 1 addition & 0 deletions libcxx/include/__atomic/atomic_ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include <__assert>
#include <__atomic/atomic_sync.h>
#include <__atomic/atomic_waitable_traits.h>
#include <__atomic/check_memory_order.h>
#include <__atomic/floating_point_helper.h>
#include <__atomic/memory_order.h>
Expand Down
85 changes: 7 additions & 78 deletions libcxx/include/__atomic/atomic_sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef _LIBCPP___ATOMIC_ATOMIC_SYNC_H
#define _LIBCPP___ATOMIC_ATOMIC_SYNC_H

#include <__atomic/atomic_waitable_traits.h>
#include <__atomic/contention_t.h>
#include <__atomic/memory_order.h>
#include <__atomic/to_gcc_order.h>
Expand All @@ -18,10 +19,8 @@
#include <__thread/poll_with_backoff.h>
#include <__type_traits/conjunction.h>
#include <__type_traits/decay.h>
#include <__type_traits/has_unique_object_representation.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_copyable.h>
#include <__type_traits/void_t.h>
#include <__utility/declval.h>
#include <cstring>
Expand All @@ -32,34 +31,6 @@

_LIBCPP_BEGIN_NAMESPACE_STD

// The customisation points to enable the following functions:
// - __atomic_wait
// - __atomic_wait_unless
// - __atomic_notify_one
// - __atomic_notify_all
// Note that std::atomic<T>::wait was back-ported to C++03
// The below implementations look ugly to support C++03
template <class _Tp, class = void>
struct __atomic_waitable_traits {
using __value_type _LIBCPP_NODEBUG = void;

template <class _AtomicWaitable>
static void __atomic_load(_AtomicWaitable&&, memory_order) = delete;

template <class _AtomicWaitable>
static void __atomic_contention_address(_AtomicWaitable&&) = delete;
};

template <class _Tp, class = void>
struct __atomic_waitable : false_type {};

template <class _Tp>
struct __atomic_waitable< _Tp,
__void_t<decltype(__atomic_waitable_traits<__decay_t<_Tp> >::__atomic_load(
std::declval<const _Tp&>(), std::declval<memory_order>())),
decltype(__atomic_waitable_traits<__decay_t<_Tp> >::__atomic_contention_address(
std::declval<const _Tp&>()))> > : true_type {};

#if _LIBCPP_STD_VER >= 20
# if _LIBCPP_HAS_THREADS

Expand Down Expand Up @@ -108,48 +79,6 @@ _LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_one
template <std::size_t _Size>
_LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_all_native(const void*) _NOEXCEPT;

# ifdef __linux__
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) _APPLY(4)
# elif defined(__APPLE__)
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) \
_APPLY(4) \
_APPLY(8)
# elif defined(__FreeBSD__) && __SIZEOF_LONG__ == 8
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) _APPLY(8)
# elif defined(_WIN32)
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) _APPLY(8)
# else
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) _APPLY(sizeof(__cxx_contention_t))
# endif // __linux__

// concepts defines the types are supported natively by the platform's wait

# 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) {
# define _LIBCPP_MAKE_CASE(n) \
case n: \
return true;
_LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_LIBCPP_MAKE_CASE)
default:
return false;
# undef _LIBCPP_MAKE_CASE
};
}

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));

# else // _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE

template <class _Tp>
concept __has_native_atomic_wait = is_same_v<_Tp, __cxx_contention_t>;

# endif // _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE

# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC

template <class _AtomicWaitable, class _Poll>
Expand Down Expand Up @@ -240,7 +169,7 @@ struct __atomic_wait_backoff_impl {
// value. The predicate function must not return `false` spuriously.
template <class _AtomicWaitable, class _Poll>
_LIBCPP_HIDE_FROM_ABI void __atomic_wait_unless(const _AtomicWaitable& __a, memory_order __order, _Poll&& __poll) {
static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
static_assert(__atomic_waitable<_AtomicWaitable>);
__atomic_wait_backoff_impl<_AtomicWaitable, __decay_t<_Poll> > __backoff_fn = {__a, __poll, __order};
std::__libcpp_thread_poll_with_backoff(
/* poll */
Expand All @@ -255,7 +184,7 @@ _LIBCPP_HIDE_FROM_ABI void __atomic_wait_unless(const _AtomicWaitable& __a, memo

template <class _AtomicWaitable>
_LIBCPP_HIDE_FROM_ABI void __atomic_notify_one(const _AtomicWaitable& __a) {
static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
static_assert(__atomic_waitable<_AtomicWaitable>);
using __value_type _LIBCPP_NODEBUG = typename __atomic_waitable_traits<__decay_t<_AtomicWaitable> >::__value_type;
using __waitable_traits _LIBCPP_NODEBUG = __atomic_waitable_traits<__decay_t<_AtomicWaitable> >;
auto __contention_address =
Expand All @@ -269,7 +198,7 @@ _LIBCPP_HIDE_FROM_ABI void __atomic_notify_one(const _AtomicWaitable& __a) {

template <class _AtomicWaitable>
_LIBCPP_HIDE_FROM_ABI void __atomic_notify_all(const _AtomicWaitable& __a) {
static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
static_assert(__atomic_waitable<_AtomicWaitable>);
using __value_type _LIBCPP_NODEBUG = typename __atomic_waitable_traits<__decay_t<_AtomicWaitable> >::__value_type;
using __waitable_traits _LIBCPP_NODEBUG = __atomic_waitable_traits<__decay_t<_AtomicWaitable> >;
auto __contention_address =
Expand All @@ -285,13 +214,13 @@ _LIBCPP_HIDE_FROM_ABI void __atomic_notify_all(const _AtomicWaitable& __a) {

template <class _AtomicWaitable>
_LIBCPP_HIDE_FROM_ABI void __atomic_notify_one(const _AtomicWaitable& __a) {
static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
static_assert(__atomic_waitable<_AtomicWaitable>);
std::__cxx_atomic_notify_one(__atomic_waitable_traits<__decay_t<_AtomicWaitable> >::__atomic_contention_address(__a));
}

template <class _AtomicWaitable>
_LIBCPP_HIDE_FROM_ABI void __atomic_notify_all(const _AtomicWaitable& __a) {
static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
static_assert(__atomic_waitable<_AtomicWaitable>);
std::__cxx_atomic_notify_all(__atomic_waitable_traits<__decay_t<_AtomicWaitable> >::__atomic_contention_address(__a));
}

Expand Down Expand Up @@ -325,7 +254,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_nonatomic_compare_equal(_Tp const& __lhs, _Tp c

template <class _AtomicWaitable, class _Tp>
_LIBCPP_HIDE_FROM_ABI void __atomic_wait(_AtomicWaitable& __a, _Tp __val, memory_order __order) {
static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
static_assert(__atomic_waitable<_AtomicWaitable>);
std::__atomic_wait_unless(__a, __order, [&](_Tp const& __current) {
return !std::__cxx_nonatomic_compare_equal(__current, __val);
});
Expand Down
100 changes: 100 additions & 0 deletions libcxx/include/__atomic/atomic_waitable_traits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___ATOMIC_ATOMIC_WAITABLE_TRAITS_H
#define _LIBCPP___ATOMIC_ATOMIC_WAITABLE_TRAITS_H

#include <__atomic/contention_t.h>
#include <__atomic/memory_order.h>
#include <__config>
#include <__type_traits/decay.h>
#include <__type_traits/has_unique_object_representation.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_copyable.h>
#include <__type_traits/void_t.h>
#include <__utility/declval.h>
#include <cstring>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 20

// The customisation points to enable the following functions:
// - __atomic_wait
// - __atomic_wait_unless
// - __atomic_notify_one
// - __atomic_notify_all
template <class _Tp, class = void>
struct __atomic_waitable_traits {
using __value_type _LIBCPP_NODEBUG = void;

template <class _AtomicWaitable>
static void __atomic_load(_AtomicWaitable&&, memory_order) = delete;

template <class _AtomicWaitable>
static void __atomic_contention_address(_AtomicWaitable&&) = delete;
};

template <class _Tp>
concept __atomic_waitable = requires(const _Tp __t, memory_order __order) {
typename __atomic_waitable_traits<__decay_t<_Tp> >::__value_type;
{ __atomic_waitable_traits<__decay_t<_Tp> >::__atomic_load(__t, __order) };
{ __atomic_waitable_traits<__decay_t<_Tp> >::__atomic_contention_address(__t) };
};

# ifdef __linux__
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) _APPLY(4)
# elif defined(__APPLE__)
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) \
_APPLY(4) \
_APPLY(8)
# elif defined(__FreeBSD__) && __SIZEOF_LONG__ == 8
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) _APPLY(8)
# elif defined(_WIN32)
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) _APPLY(8)
# else
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) _APPLY(sizeof(__cxx_contention_t))
# endif // __linux__

// concepts defines the types are supported natively by the platform's wait

# 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) {
# define _LIBCPP_MAKE_CASE(n) \
case n: \
return true;
_LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_LIBCPP_MAKE_CASE)
default:
return false;
# undef _LIBCPP_MAKE_CASE
};
}

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));

# else // _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE

template <class _Tp>
concept __has_native_atomic_wait = is_same_v<_Tp, __cxx_contention_t>;

# endif // _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE

#endif // C++20

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___ATOMIC_ATOMIC_WAITABLE_TRAITS_H
1 change: 1 addition & 0 deletions libcxx/include/atomic
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ template <class T>
# include <__atomic/atomic_init.h>
# include <__atomic/atomic_lock_free.h>
# include <__atomic/atomic_sync.h>
# include <__atomic/atomic_waitable_traits.h>
# include <__atomic/check_memory_order.h>
# include <__atomic/contention_t.h>
# include <__atomic/fence.h>
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/module.modulemap.in
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,7 @@ module std [system] {
module atomic_lock_free { header "__atomic/atomic_lock_free.h" }
module atomic_ref { header "__atomic/atomic_ref.h" }
module atomic_sync { header "__atomic/atomic_sync.h" }
module atomic_waitable_traits { header "__atomic/atomic_waitable_traits.h" }
module atomic {
header "__atomic/atomic.h"
export std.atomic.atomic_base // most of std::atomic methods are defined there
Expand Down
Loading