[libcxx-commits] [libcxx] 2737061 - [libc++][pstl] Implementation of parallel std::destroy() and std::destroy_n() (#211888)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Aug 5 08:17:14 PDT 2026
Author: Michael G. Kazakov
Date: 2026-08-05T11:17:08-04:00
New Revision: 273706151fa625e1faa97e5f50b01867b5211e92
URL: https://github.com/llvm/llvm-project/commit/273706151fa625e1faa97e5f50b01867b5211e92
DIFF: https://github.com/llvm/llvm-project/commit/273706151fa625e1faa97e5f50b01867b5211e92.diff
LOG: [libc++][pstl] Implementation of parallel std::destroy() and std::destroy_n() (#211888)
This PR adds an implementation of parallel versions of `std::destroy`
and `std::destroy_n`. The implementations are based on `for_each` and
`for_each_n`, effectively one-liners
Fixes #134588.
Fixes #134589.
Fixes #101693.
Added:
libcxx/include/__memory/pstl.h
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy.pass.cpp
libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy_n.pass.cpp
Modified:
libcxx/docs/Status/Cxx20Issues.csv
libcxx/include/CMakeLists.txt
libcxx/include/__pstl/backend_fwd.h
libcxx/include/__pstl/backends/default.h
libcxx/include/memory
libcxx/include/module.modulemap.in
libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
libcxx/test/libcxx/transitive_includes/cxx17.csv
libcxx/test/libcxx/transitive_includes/cxx20.csv
libcxx/test/libcxx/transitive_includes/cxx23.csv
libcxx/test/libcxx/transitive_includes/cxx26.csv
libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index 229c16065939f..3b7c138e4bd81 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -231,7 +231,7 @@
"`LWG3300 <https://wg21.link/LWG3300>`__","Non-array ``ssize``\ overload is underconstrained","2020-02 (Prague)","|Nothing To Do|","","`#103929 <https://github.com/llvm/llvm-project/issues/103929>`__",""
"`LWG3301 <https://wg21.link/LWG3301>`__","``transform_view::iterator``\ has incorrect ``iterator_category``\ ","2020-02 (Prague)","|Complete|","15","`#103932 <https://github.com/llvm/llvm-project/issues/103932>`__",""
"`LWG3302 <https://wg21.link/LWG3302>`__","Range adaptor objects ``keys``\ and ``values``\ are unspecified","2020-02 (Prague)","|Complete|","16","`#103933 <https://github.com/llvm/llvm-project/issues/103933>`__",""
-"`LWG3303 <https://wg21.link/LWG3303>`__","Bad ""``constexpr``\ "" marker for ``destroy/destroy_n``\ ","2020-02 (Prague)","","","`#101693 <https://github.com/llvm/llvm-project/issues/101693>`__",""
+"`LWG3303 <https://wg21.link/LWG3303>`__","Bad ""``constexpr``\ "" marker for ``destroy/destroy_n``\ ","2020-02 (Prague)","|Complete|","24","`#101693 <https://github.com/llvm/llvm-project/issues/101693>`__",""
"`LWG3304 <https://wg21.link/LWG3304>`__","Allocate functions of ``std::polymorphic_allocator``\ should require ``[[nodiscard]]``\ ","2020-02 (Prague)","|Complete|","16","`#103934 <https://github.com/llvm/llvm-project/issues/103934>`__",""
"`LWG3307 <https://wg21.link/LWG3307>`__","``std::allocator<void>().allocate(n)``\ ","2020-02 (Prague)","|Complete|","20","`#100422 <https://github.com/llvm/llvm-project/issues/100422>`__",""
"`LWG3310 <https://wg21.link/LWG3310>`__","Replace ``SIZE_MAX``\ with ``numeric_limits<size_t>::max()``\ ","2020-02 (Prague)","|Complete|","16","`#103936 <https://github.com/llvm/llvm-project/issues/103936>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index f5f07c24c8b1d..95d371c092983 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -618,6 +618,7 @@ set(files
__memory/noexcept_move_assign_container.h
__memory/out_ptr.h
__memory/pointer_traits.h
+ __memory/pstl.h
__memory/ranges_construct_at.h
__memory/ranges_destroy.h
__memory/ranges_uninitialized_algorithms.h
diff --git a/libcxx/include/__memory/pstl.h b/libcxx/include/__memory/pstl.h
new file mode 100644
index 0000000000000..a4fc727f3bf72
--- /dev/null
+++ b/libcxx/include/__memory/pstl.h
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___MEMORY_PSTL_H
+#define _LIBCPP___MEMORY_PSTL_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
+
+# include <__iterator/cpp17_iterator_concepts.h>
+# include <__iterator/iterator_traits.h>
+# include <__pstl/backend.h>
+# include <__pstl/dispatch.h>
+# include <__pstl/handle_exception.h>
+# include <__type_traits/enable_if.h>
+# include <__type_traits/is_execution_policy.h>
+# include <__type_traits/remove_cvref.h>
+# include <__utility/forward.h>
+# include <__utility/move.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI void destroy(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "destroy requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__destroy, __pstl::__current_configuration, _RawPolicy>;
+ __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last));
+}
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator,
+ class _Size,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI void destroy_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "destroy_n requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__destroy_n, __pstl::__current_configuration, _RawPolicy>;
+ __pstl::__handle_exception<_Implementation>(std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_PSTL_H
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index e83375d1c0c74..c5a219fb59db2 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -365,6 +365,18 @@ struct __lexicographical_compare;
// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
// _ForwardIterator2 __first2, _ForwardIterator2 __last2, _Comp __comp) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __destroy;
+// template <class _Policy, class _ForwardIterator>
+// optional<__empty>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last) const noexcept;
+
+template <class _Backend, class _ExecutionPolicy>
+struct __destroy_n;
+// template <class _Policy, class _ForwardIterator, class _Size>
+// optional<__empty>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept;
+
} // namespace __pstl
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 5562c23bf09f1..43bac357efd95 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -27,6 +27,8 @@
#include <__iterator/next.h>
#include <__iterator/prev.h>
#include <__iterator/reverse_iterator.h>
+#include <__memory/addressof.h>
+#include <__memory/construct_at.h>
#include <__optional/comparison.h>
#include <__optional/nullopt_t.h>
#include <__optional/optional.h>
@@ -78,6 +80,8 @@ namespace __pstl {
//
// for_each family
// ---------------
+// - destroy
+// - destroy_n
// - for_each_n
// - fill
// - fill_n
@@ -368,6 +372,31 @@ struct __is_sorted<__default_backend_tag, _ExecutionPolicy> {
//////////////////////////////////////////////////////////////
// for_each family
//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __destroy<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator>
+ optional<__empty> operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last) const noexcept {
+ using _ForEach = __dispatch<__for_each, __current_configuration, _ExecutionPolicy>;
+ using _Ref = __iterator_reference<_ForwardIterator>;
+ return _ForEach()(__policy, std::move(__first), std::move(__last), [&](_Ref __element) {
+ std::destroy_at(std::addressof(__element));
+ });
+ }
+};
+
+template <class _ExecutionPolicy>
+struct __destroy_n<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator, class _Size>
+ optional<__empty> operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept {
+ using _ForEachN = __dispatch<__for_each_n, __current_configuration, _ExecutionPolicy>;
+ using _Ref = __iterator_reference<_ForwardIterator>;
+ return _ForEachN()(__policy, std::move(__first), __n, [&](_Ref __element) {
+ std::destroy_at(std::addressof(__element));
+ });
+ }
+};
+
template <class _ExecutionPolicy>
struct __for_each_n<__default_backend_tag, _ExecutionPolicy> {
template <class _Policy, class _ForwardIterator, class _Size, class _Function>
diff --git a/libcxx/include/memory b/libcxx/include/memory
index 65765c948b96c..e1e2e801a07ab 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -994,6 +994,7 @@ template<class Pointer = void, class Smart, class... Args>
# if _LIBCPP_STD_VER >= 17
# include <__memory/construct_at.h>
# include <__memory/destroy.h>
+# include <__memory/pstl.h>
# endif
# if _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 97467ca5757e4..8e568e04f4d69 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1700,6 +1700,7 @@ module std {
module noexcept_move_assign_container { header "__memory/noexcept_move_assign_container.h" }
module out_ptr { header "__memory/out_ptr.h" }
module pointer_traits { header "__memory/pointer_traits.h" }
+ module pstl { header "__memory/pstl.h" }
module ranges_construct_at { header "__memory/ranges_construct_at.h" }
module ranges_destroy { header "__memory/ranges_destroy.h" }
module ranges_uninitialized_algorithms {
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e61c562025fa0..2d510be58e164 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -28,6 +28,7 @@
#include <cstddef>
#include <execution>
#include <numeric>
+#include <memory>
#include "test_iterators.h"
@@ -70,6 +71,11 @@ void f(non_forward_iterator non_fwd,
(void)std::count_if(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: count_if}}
}
+ {
+ (void)std::destroy(pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: destroy}}
+ (void)std::destroy_n(pol, non_fwd, n); // expected-error@*:* {{static assertion failed: destroy_n}}
+ }
+
{
(void)std::equal(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: equal}}
(void)std::equal(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: equal}}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index e12ca042800ab..e0a92259202c4 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -527,11 +527,17 @@ map stdexcept
map tuple
map version
mdspan version
+memory cctype
+memory climits
memory compare
memory cstdint
memory cstring
+memory ctime
+memory cwchar
+memory cwctype
memory initializer_list
memory limits
+memory ratio
memory tuple
memory typeinfo
memory version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 29499c037e5c2..44b5fe1f1577f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -592,11 +592,17 @@ map stdexcept
map tuple
map version
mdspan version
+memory cctype
+memory climits
memory compare
memory cstdint
memory cstring
+memory ctime
+memory cwchar
+memory cwctype
memory initializer_list
memory limits
+memory ratio
memory tuple
memory typeinfo
memory version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 6658fa9c641a4..a199c610e9f57 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -626,11 +626,17 @@ mdspan limits
mdspan span
mdspan stdexcept
mdspan version
+memory cctype
+memory climits
memory compare
memory cstdint
memory cstring
+memory ctime
+memory cwchar
+memory cwctype
memory initializer_list
memory limits
+memory ratio
memory tuple
memory typeinfo
memory version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 87a446bafbdfb..62b58acd1e58e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -619,11 +619,17 @@ mdspan limits
mdspan span
mdspan stdexcept
mdspan version
+memory cctype
+memory climits
memory compare
memory cstdint
memory cstring
+memory ctime
+memory cwchar
+memory cwctype
memory initializer_list
memory limits
+memory ratio
memory tuple
memory typeinfo
memory version
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 063210a13af7e..19e8cb9f8b0d1 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <numeric>
+#include <memory>
#include "check_assertion.h"
#include "test_execution_policies.h"
@@ -127,6 +128,14 @@ int main(int, char**) {
assert_non_throwing([=, &policy] { (void)std::count_if(policy, std::move(first1), std::move(last1), pred); });
}
+ {
+ // destroy(first, last)
+ assert_non_throwing([=, &policy] { (void)std::destroy(policy, std::move(first1), std::move(last1)); });
+
+ // destroy_n(first, n)
+ assert_non_throwing([=, &policy] { (void)std::destroy_n(policy, std::move(first1), n); });
+ }
+
{
auto binary_pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy.pass.cpp
new file mode 100644
index 0000000000000..f36a404bb9797
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy.pass.cpp
@@ -0,0 +1,150 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+// class ForwardIterator>
+// void destroy(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(destroy);
+
+static_assert(sfinae_test_destroy<int, int*, int*>);
+static_assert(!sfinae_test_destroy<std::execution::parallel_policy, int*, int*>);
+
+struct Counted {
+ std::atomic_int* counter_;
+ Counted(std::atomic_int* counter) : counter_(counter) { counter_->fetch_add(1); }
+ Counted(Counted const& other) : counter_(other.counter_) { counter_->fetch_add(1); }
+ ~Counted() { counter_->fetch_sub(1); }
+ friend void operator&(Counted) = delete;
+};
+
+template <class Iter>
+struct TestCounted {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ // Test destroy() with a range of Counted objects.
+ // Ranges vary in size from 0 to 1073.
+ // Each object has its own counter.
+ std::atomic_int counters[1073];
+ std::fill_n(std::begin(counters), std::size(counters), 0);
+
+ using Alloc = std::allocator<Counted>;
+ Alloc alloc;
+ Counted* pool = std::allocator_traits<Alloc>::allocate(alloc, std::size(counters));
+
+ runway_sample(std::size(counters) + 1, [&](size_t size) {
+ for (std::size_t i = 0; i < size; ++i) {
+ std::allocator_traits<Alloc>::construct(alloc, std::addressof(pool[i]), &counters[i]);
+ }
+ assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 1; }));
+
+ std::destroy(policy, Iter(pool), Iter(pool + size));
+ ASSERT_SAME_TYPE(decltype(std::destroy(policy, Iter(pool), Iter(pool + size))), void);
+ assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 0; }));
+ });
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, std::size(counters));
+ }
+ }
+};
+
+// std::destroy on a sequence of arrays is supported since C++20.
+#if TEST_STD_VER >= 20
+template <class Iter>
+struct TestArrayCounted3 {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ // Test destroy() with a range of 5 Counted[3] objects.
+ // A shared counter is used for all objects.
+ using Array = Counted[3];
+ using Alloc = std::allocator<Array>;
+ std::atomic_int counter = 0;
+ Alloc alloc;
+ Array* pool = std::allocator_traits<Alloc>::allocate(alloc, 5);
+
+ for (Array* p = pool; p != pool + 5; ++p) {
+ Array& arr = *p;
+ for (int i = 0; i != 3; ++i) {
+ std::allocator_traits<Alloc>::construct(alloc, std::addressof(arr[i]), &counter);
+ }
+ }
+ assert(counter == 5 * 3);
+
+ std::destroy(policy, Iter(pool), Iter(pool + 5));
+ ASSERT_SAME_TYPE(decltype(std::destroy(policy, Iter(pool), Iter(pool + 5))), void);
+ assert(counter == 0);
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, 5);
+ }
+ }
+};
+
+template <class Iter>
+struct TestArrayCounted3x2 {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ // Test destroy() with a range of 5 Counted[3][2] objects.
+ // A shared counter is used for all objects.
+ using Array = Counted[3][2];
+ using Alloc = std::allocator<Array>;
+ std::atomic_int counter = 0;
+ Alloc alloc;
+ Array* pool = std::allocator_traits<Alloc>::allocate(alloc, 5);
+
+ for (Array* p = pool; p != pool + 5; ++p) {
+ Array& arr = *p;
+ for (int i = 0; i != 3; ++i) {
+ for (int j = 0; j != 2; ++j) {
+ std::allocator_traits<Alloc>::construct(alloc, std::addressof(arr[i][j]), &counter);
+ }
+ }
+ }
+ assert(counter == 5 * 3 * 2);
+
+ std::destroy(policy, Iter(pool), Iter(pool + 5));
+ ASSERT_SAME_TYPE(decltype(std::destroy(policy, Iter(pool), Iter(pool + 5))), void);
+ assert(counter == 0);
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, 5);
+ }
+ }
+};
+#endif // TEST_STD_VER >= 20
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<Counted*>{}, TestIteratorWithPolicies<TestCounted>{});
+#if TEST_STD_VER >= 20
+ using CountedArray3 = Counted[3];
+ types::for_each(types::forward_iterator_list<CountedArray3*>{}, TestIteratorWithPolicies<TestArrayCounted3>{});
+ using CountedArray3x2 = Counted[3][2];
+ types::for_each(types::forward_iterator_list<CountedArray3x2*>{}, TestIteratorWithPolicies<TestArrayCounted3x2>{});
+#endif // TEST_STD_VER >= 20
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy_n.pass.cpp
new file mode 100644
index 0000000000000..bcbdb65bb0e2f
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/pstl.destroy_n.pass.cpp
@@ -0,0 +1,150 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// template <class ExecutionPolicy,
+// class ForwardIterator,
+// class Size>
+// void destroy_n(ExecutionPolicy&& exec, ForwardIterator first, Size n);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(destroy_n);
+
+static_assert(sfinae_test_destroy_n<int, int*, int>);
+static_assert(!sfinae_test_destroy_n<std::execution::parallel_policy, int*, int>);
+
+struct Counted {
+ std::atomic_int* counter_;
+ Counted(std::atomic_int* counter) : counter_(counter) { counter_->fetch_add(1); }
+ Counted(Counted const& other) : counter_(other.counter_) { counter_->fetch_add(1); }
+ ~Counted() { counter_->fetch_sub(1); }
+ friend void operator&(Counted) = delete;
+};
+
+template <class Iter>
+struct TestCounted {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ // Test destroy_n() with a range of Counted objects.
+ // Ranges vary in size from 0 to 1073.
+ // Each object has its own counter.
+ std::atomic_int counters[1073];
+ std::fill_n(std::begin(counters), std::size(counters), 0);
+
+ using Alloc = std::allocator<Counted>;
+ Alloc alloc;
+ Counted* pool = std::allocator_traits<Alloc>::allocate(alloc, std::size(counters));
+
+ runway_sample(std::size(counters) + 1, [&](size_t size) {
+ for (std::size_t i = 0; i < size; ++i) {
+ std::allocator_traits<Alloc>::construct(alloc, std::addressof(pool[i]), &counters[i]);
+ }
+ assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 1; }));
+
+ std::destroy_n(policy, Iter(pool), size);
+ ASSERT_SAME_TYPE(decltype(std::destroy_n(policy, Iter(pool), size)), void);
+ assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 0; }));
+ });
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, std::size(counters));
+ }
+ }
+};
+
+// std::destroy_n on a sequence of arrays is supported since C++20.
+#if TEST_STD_VER >= 20
+template <class Iter>
+struct TestArrayCounted3 {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ // Test destroy_n() with a range of 5 Counted[3] objects.
+ // A shared counter is used for all objects.
+ using Array = Counted[3];
+ using Alloc = std::allocator<Array>;
+ std::atomic_int counter = 0;
+ Alloc alloc;
+ Array* pool = std::allocator_traits<Alloc>::allocate(alloc, 5);
+
+ for (Array* p = pool; p != pool + 5; ++p) {
+ Array& arr = *p;
+ for (int i = 0; i != 3; ++i) {
+ std::allocator_traits<Alloc>::construct(alloc, std::addressof(arr[i]), &counter);
+ }
+ }
+ assert(counter == 5 * 3);
+
+ std::destroy_n(policy, Iter(pool), 5);
+ ASSERT_SAME_TYPE(decltype(std::destroy_n(policy, Iter(pool), 5)), void);
+ assert(counter == 0);
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, 5);
+ }
+ }
+};
+
+template <class Iter>
+struct TestArrayCounted3x2 {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ // Test destroy_n() with a range of 5 Counted[3][2] objects.
+ // A shared counter is used for all objects.
+ using Array = Counted[3][2];
+ using Alloc = std::allocator<Array>;
+ std::atomic_int counter = 0;
+ Alloc alloc;
+ Array* pool = std::allocator_traits<Alloc>::allocate(alloc, 5);
+
+ for (Array* p = pool; p != pool + 5; ++p) {
+ Array& arr = *p;
+ for (int i = 0; i != 3; ++i) {
+ for (int j = 0; j != 2; ++j) {
+ std::allocator_traits<Alloc>::construct(alloc, std::addressof(arr[i][j]), &counter);
+ }
+ }
+ }
+ assert(counter == 5 * 3 * 2);
+ std::destroy_n(policy, Iter(pool), 5);
+ ASSERT_SAME_TYPE(decltype(std::destroy_n(policy, Iter(pool), 5)), void);
+ assert(counter == 0);
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, 5);
+ }
+ }
+};
+#endif // TEST_STD_VER >= 20
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<Counted*>{}, TestIteratorWithPolicies<TestCounted>{});
+#if TEST_STD_VER >= 20
+ using CountedArray3 = Counted[3];
+ types::for_each(types::forward_iterator_list<CountedArray3*>{}, TestIteratorWithPolicies<TestArrayCounted3>{});
+ using CountedArray3x2 = Counted[3][2];
+ types::for_each(types::forward_iterator_list<CountedArray3x2*>{}, TestIteratorWithPolicies<TestArrayCounted3x2>{});
+#endif // TEST_STD_VER >= 20
+ return 0;
+}
More information about the libcxx-commits
mailing list