diff --git a/stl/inc/atomic b/stl/inc/atomic index 609fa020436..133e7fcd73e 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -2018,11 +2018,27 @@ using atomic_ptrdiff_t = atomic; using atomic_intmax_t = atomic; using atomic_uintmax_t = atomic; +#if _HAS_CXX20 +// Though there are CMPXCHG8B and CMPXCHG16B, +// the largest atomics with a full set of efficient operations are pointer-sized. +using atomic_signed_lock_free = atomic_intptr_t; +using atomic_unsigned_lock_free = atomic_uintptr_t; +#endif // _HAS_CXX20 // STRUCT atomic_flag #define ATOMIC_FLAG_INIT \ {} struct atomic_flag { // flag with test-and-set semantics +#if _HAS_CXX20 + _NODISCARD bool test(const memory_order _Order = memory_order_seq_cst) const noexcept { + return _Storage.load(_Order) != 0; + } + + _NODISCARD bool test(const memory_order _Order = memory_order_seq_cst) const volatile noexcept { + return _Storage.load(_Order) != 0; + } +#endif // _HAS_CXX20 + bool test_and_set(const memory_order _Order = memory_order_seq_cst) noexcept { return _Storage.exchange(true, _Order) != 0; } @@ -2050,6 +2066,25 @@ struct atomic_flag { // flag with test-and-set semantics // atomic_flag NONMEMBERS +#if _HAS_CXX20 +_NODISCARD inline bool atomic_flag_test(const volatile atomic_flag* const _Flag) noexcept { + return _Flag->test(); +} + +_NODISCARD inline bool atomic_flag_test(const atomic_flag* const _Flag) noexcept { + return _Flag->test(); +} + +_NODISCARD inline bool atomic_flag_test_explicit( + const volatile atomic_flag* const _Flag, const memory_order _Order) noexcept { + return _Flag->test(_Order); +} + +_NODISCARD inline bool atomic_flag_test_explicit(const atomic_flag* const _Flag, const memory_order _Order) noexcept { + return _Flag->test(_Order); +} +#endif // _HAS_CXX20 + inline bool atomic_flag_test_and_set(atomic_flag* _Flag) noexcept { return _Flag->test_and_set(); } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 73e5052098d..1c10b505836 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1130,10 +1130,12 @@ #define __cpp_lib_atomic_value_initialization 201911L #if _HAS_CXX20 -#define __cpp_lib_atomic_float 201711L -#define __cpp_lib_atomic_shared_ptr 201711L -#define __cpp_lib_bind_front 201907L -#define __cpp_lib_bit_cast 201806L +#define __cpp_lib_atomic_flag_test 201907L +#define __cpp_lib_atomic_float 201711L +#define __cpp_lib_atomic_lock_free_type_aliases 201907L +#define __cpp_lib_atomic_shared_ptr 201711L +#define __cpp_lib_bind_front 201907L +#define __cpp_lib_bit_cast 201806L #ifdef __clang__ // TRANSITION, VSO-1020212 // a future MSVC update will embed CPU feature detection into intrinsics diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 7509d11b446..f3f01ac71b1 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -504,7 +504,6 @@ std/strings/char.traits/char.traits.specializations/char.traits.specializations. std/strings/char.traits/char.traits.specializations/char.traits.specializations.wchar.t/move.pass.cpp FAIL # C++20 P1135R6 "The C++20 Synchronization Library" -std/atomics/types.pass.cpp FAIL std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp FAIL std/thread/thread.barrier/arrive.pass.cpp FAIL std/thread/thread.barrier/arrive_and_drop.pass.cpp FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 7fc7f0502aa..8369ae8967c 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -504,7 +504,6 @@ strings\char.traits\char.traits.specializations\char.traits.specializations.wcha strings\char.traits\char.traits.specializations\char.traits.specializations.wchar.t\move.pass.cpp # C++20 P1135R6 "The C++20 Synchronization Library" -atomics\types.pass.cpp atomics\atomics.types.operations\atomics.types.operations.wait\atomic_wait.pass.cpp thread\thread.barrier\arrive.pass.cpp thread\thread.barrier\arrive_and_drop.pass.cpp diff --git a/tests/std/test.lst b/tests/std/test.lst index 9cdaa15d150..d3ef6c082f6 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -254,6 +254,7 @@ tests\P0898R3_identity tests\P0919R3_heterogeneous_unordered_lookup tests\P0966R1_string_reserve_should_not_shrink tests\P1023R0_constexpr_for_array_comparisons +tests\P1135R6_atomic_flag_test tests\P1165R1_consistently_propagating_stateful_allocators tests\P1423R3_char8_t_remediation tests\P1645R1_constexpr_numeric diff --git a/tests/std/tests/P1135R6_atomic_flag_test/env.lst b/tests/std/tests/P1135R6_atomic_flag_test/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P1135R6_atomic_flag_test/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P1135R6_atomic_flag_test/test.cpp b/tests/std/tests/P1135R6_atomic_flag_test/test.cpp new file mode 100644 index 00000000000..f82dbed3108 --- /dev/null +++ b/tests/std/tests/P1135R6_atomic_flag_test/test.cpp @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +constexpr auto mo = std::memory_order_seq_cst; + +template +void test_flags(const IsSet is_set, const TestAndSet test_and_set, const Clear clear) { + constexpr std::size_t unique = 800; + constexpr std::size_t repetitions = 800; + constexpr std::size_t total = unique * repetitions; + constexpr std::size_t dups = total - unique; + + FlagType flags[unique]; + std::vector ptrs; + ptrs.reserve(total); + for (std::size_t i = 0; i != repetitions; ++i) { + for (auto& flag : flags) { + ptrs.push_back(&flag); + } + } + + using std::execution::par; + + assert(std::transform_reduce(par, ptrs.begin(), ptrs.end(), 0, std::plus{}, is_set) == 0); + assert(std::transform_reduce(par, ptrs.begin(), ptrs.end(), 0, std::plus{}, test_and_set) == dups); + assert(std::transform_reduce(par, ptrs.begin(), ptrs.end(), 0, std::plus{}, is_set) == total); + assert(std::transform_reduce(par, ptrs.begin(), ptrs.end(), 0, std::plus{}, test_and_set) == total); + std::for_each(par, ptrs.begin(), ptrs.end(), clear); + assert(std::transform_reduce(par, ptrs.begin(), ptrs.end(), 0, std::plus{}, is_set) == 0); +} + +template +void test_flags_members() { + const auto is_set = [](const FlagType* f) { return f->test(); }; + const auto test_and_set = [](FlagType* f) { return f->test_and_set(); }; + const auto clear = [](FlagType* f) { f->clear(); }; + + test_flags(is_set, test_and_set, clear); +} + +template +void test_flags_members_mo() { + const auto is_set = [](const FlagType* f) { return f->test(mo); }; + const auto test_and_set = [](FlagType* f) { return f->test_and_set(mo); }; + const auto clear = [](FlagType* f) { f->clear(mo); }; + + test_flags(is_set, test_and_set, clear); +} + +template +void test_flags_free() { + const auto is_set = [](const FlagType* f) { return std::atomic_flag_test(f); }; + const auto test_and_set = [](FlagType* f) { return std::atomic_flag_test_and_set(f); }; + const auto clear = [](FlagType* f) { std::atomic_flag_clear(f); }; + + test_flags(is_set, test_and_set, clear); +} + +template +void test_flags_free_mo() { + const auto is_set = [](const FlagType* f) { return std::atomic_flag_test_explicit(f, mo); }; + const auto test_and_set = [](FlagType* f) { return std::atomic_flag_test_and_set_explicit(f, mo); }; + const auto clear = [](FlagType* f) { std::atomic_flag_clear_explicit(f, mo); }; + + test_flags(is_set, test_and_set, clear); +} + +template +void test_flag_type() { + test_flags_members(); + test_flags_free(); + test_flags_members_mo(); + test_flags_free_mo(); +} + +int main() { + test_flag_type(); + test_flag_type(); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp index 57134316e3d..5ee309c3d74 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp @@ -83,6 +83,20 @@ STATIC_ASSERT(__cpp_lib_array_constexpr == 201803L); STATIC_ASSERT(__cpp_lib_as_const == 201510L); #endif +#if _HAS_CXX20 +#ifndef __cpp_lib_atomic_flag_test +#error __cpp_lib_atomic_flag_test is not defined +#elif __cpp_lib_atomic_flag_test != 201907L +#error __cpp_lib_atomic_flag_test is not 201907L +#else +STATIC_ASSERT(__cpp_lib_atomic_flag_test == 201907L); +#endif +#else +#ifdef __cpp_lib_atomic_flag_test +#error __cpp_lib_atomic_flag_test is defined +#endif +#endif + #if _HAS_CXX20 #ifndef __cpp_lib_atomic_float #error __cpp_lib_atomic_float is not defined @@ -111,6 +125,20 @@ STATIC_ASSERT(__cpp_lib_atomic_is_always_lock_free == 201603L); #endif #endif +#if _HAS_CXX20 +#ifndef __cpp_lib_atomic_lock_free_type_aliases +#error __cpp_lib_atomic_lock_free_type_aliases is not defined +#elif __cpp_lib_atomic_lock_free_type_aliases != 201907L +#error __cpp_lib_atomic_lock_free_type_aliases is not 201907L +#else +STATIC_ASSERT(__cpp_lib_atomic_lock_free_type_aliases == 201907L); +#endif +#else +#ifdef __cpp_lib_atomic_lock_free_type_aliases +#error __cpp_lib_atomic_lock_free_type_aliases is defined +#endif +#endif + #if _HAS_CXX20 #ifndef __cpp_lib_atomic_shared_ptr #error __cpp_lib_atomic_shared_ptr is not defined