diff --git a/stl/inc/execution b/stl/inc/execution index efebbcea021..06461dc72dd 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -110,7 +110,8 @@ namespace execution { // indicates support by element access functions for weakly parallel forward progress guarantees, and for // executing interleaved on the same thread, and requests termination on exceptions // - // (at this time, equivalent to sequenced_policy except for the for_each family) + // (at this time, equivalent to sequenced_policy except for for_each(_n), destroy(_n), + // uninitialized_default_construct(_n), and uninitialized_value_construct(_n)) public: using _Standard_execution_policy = int; static constexpr bool _Parallelize = false; @@ -5050,6 +5051,166 @@ _FwdIt2 adjacent_difference(_ExPo&&, const _FwdIt1 _First, const _FwdIt1 _Last, _Get_unwrapped_n(_Dest, _Idl_distance<_FwdIt1>(_UFirst, _ULast)), _Pass_fn(_Diff_op))); return _Dest; } + +_EXPORT_STD template /* = 0 */> +void destroy(_ExPo&& _Exec, const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) noexcept /* terminates */ { + // destroy all elements in [_First, _Last) + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + _STD _Adl_verify_range(_First, _Last); + using _Ty = remove_reference_t<_Iter_ref_t<_NoThrowFwdIt>>; + if constexpr (!is_trivially_destructible_v<_Ty>) { + _STD for_each(_STD forward<_ExPo>(_Exec), _First, _Last, [](_Ty& _Obj) { _STD _Destroy_in_place(_Obj); }); + } +} + +_EXPORT_STD template /* = 0 */> +_NoThrowFwdIt destroy_n(_ExPo&& _Exec, _NoThrowFwdIt _First, const _Diff _Count_raw) noexcept /* terminates */ { + // destroy all elements in [_First, _First + _Count) + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + _Algorithm_int_t<_Diff> _Count = _Count_raw; + if (_Count <= 0) { + return _First; + } + + using _Ty = remove_reference_t<_Iter_ref_t<_NoThrowFwdIt>>; + if constexpr (is_trivially_destructible_v<_Ty>) { + _STD advance(_First, _Count); + return _First; + } else { + return _STD for_each_n( + _STD forward<_ExPo>(_Exec), _First, _Count, [](_Ty& _Obj) { _STD _Destroy_in_place(_Obj); }); + } +} + +_EXPORT_STD template /* = 0 */> +void uninitialized_default_construct(_ExPo&& _Exec, const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) noexcept +/* terminates */ { + // default-initialize all elements in [_First, _Last) + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + _STD _Adl_verify_range(_First, _Last); + using _Ty = remove_reference_t<_Iter_ref_t<_NoThrowFwdIt>>; + if constexpr (!is_trivially_default_constructible_v<_Ty>) { + _STD for_each( + _STD forward<_ExPo>(_Exec), _First, _Last, [](_Ty& _Obj) { _STD _Default_construct_in_place(_Obj); }); + } +} + +_EXPORT_STD template /* = 0 */> +_NoThrowFwdIt uninitialized_default_construct_n(_ExPo&& _Exec, _NoThrowFwdIt _First, const _Diff _Count_raw) noexcept +/* terminates */ { + // default-initialize all elements in [_First, _First + _Count_raw) + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + _Algorithm_int_t<_Diff> _Count = _Count_raw; + if (_Count <= 0) { + return _First; + } + + using _Ty = remove_reference_t<_Iter_ref_t<_NoThrowFwdIt>>; + if constexpr (is_trivially_default_constructible_v<_Ty>) { + _STD advance(_First, _Count); + return _First; + } else { + return _STD for_each_n( + _STD forward<_ExPo>(_Exec), _First, _Count, [](_Ty& _Obj) { _STD _Default_construct_in_place(_Obj); }); + } +} + +_EXPORT_STD template /* = 0 */> +void uninitialized_value_construct(_ExPo&&, _NoThrowFwdIt _First, _NoThrowFwdIt _Last) noexcept /* terminates */ { + // value-initialize all elements in [_First, _Last) + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + _STD _Adl_verify_range(_First, _Last); + auto _UFirst = _STD _Get_unwrapped(_First); + const auto _ULast = _STD _Get_unwrapped(_Last); + + if constexpr (_Use_memset_value_construct_v) { + _STD _Zero_range(_UFirst, _ULast); + } else if constexpr (remove_reference_t<_ExPo>::_Parallelize) { + const size_t _Hw_threads = _CSTD __std_parallel_algorithms_hw_threads(); + if (_Hw_threads > 1) { // parallelize on multiprocessor machines... + auto _Count = _STD distance(_UFirst, _ULast); + if (_Count >= 2) { // ... with at least 2 elements + _TRY_BEGIN + using _Ty = remove_reference_t<_Iter_ref_t<_NoThrowFwdIt>>; + auto _Ctor_fn = [](_Ty& _Obj) { _STD _Construct_in_place(_Obj); }; + _Static_partitioned_for_each2 _Operation{ + _Hw_threads, _Count, _Ctor_fn}; + _Operation._Basis._Populate(_Operation._Team, _UFirst); + _STD _Run_chunked_parallel_work(_Hw_threads, _Operation); + return; + _CATCH(const _Parallelism_resources_exhausted&) + // fall through to serial case below + _CATCH_END + } + } + +#pragma loop(ivdep) + for (; _UFirst != _ULast; ++_UFirst) { + _STD _Construct_in_place(*_UFirst); + } + } else if constexpr (remove_reference_t<_ExPo>::_Ivdep) { +#pragma loop(ivdep) + for (; _UFirst != _ULast; ++_UFirst) { + _STD _Construct_in_place(*_UFirst); + } + } else { + for (; _UFirst != _ULast; ++_UFirst) { + _STD _Construct_in_place(*_UFirst); + } + } +} + +_EXPORT_STD template /* = 0 */> +_NoThrowFwdIt uninitialized_value_construct_n(_ExPo&&, _NoThrowFwdIt _First, const _Diff _Count_raw) noexcept +/* terminates */ { + // value-initialize all elements in [_First, _First + _Count_raw) + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + _Algorithm_int_t<_Diff> _Count = _Count_raw; + + if (_Count <= 0) { + return _First; + } + + auto _UFirst = _STD _Get_unwrapped_n(_First, _Count); + if constexpr (_Use_memset_value_construct_v) { + _STD _Zero_range(_UFirst, _UFirst + _Count); + _STD _Seek_wrapped(_First, _UFirst + _Count); + } else if constexpr (remove_reference_t<_ExPo>::_Parallelize) { + const size_t _Hw_threads = _CSTD __std_parallel_algorithms_hw_threads(); + if (_Hw_threads > 1 && _Count >= 2) { // parallelize on multiprocessor machines with at least 2 elements + _TRY_BEGIN + using _Ty = remove_reference_t<_Iter_ref_t<_NoThrowFwdIt>>; + auto _Ctor_fn = [](_Ty& _Obj) { _STD _Construct_in_place(_Obj); }; + _Static_partitioned_for_each2 _Operation{ + _Hw_threads, _Count, _Ctor_fn}; + _STD _Seek_wrapped(_First, _Operation._Basis._Populate(_Operation._Team, _UFirst)); + _STD _Run_chunked_parallel_work(_Hw_threads, _Operation); + return _First; + _CATCH(const _Parallelism_resources_exhausted&) + // fall through to serial case below + _CATCH_END + } + +#pragma loop(ivdep) + for (; _Count > 0; --_Count, (void) ++_UFirst) { + _STD _Construct_in_place(*_UFirst); + } + _STD _Seek_wrapped(_First, _UFirst); + } else if constexpr (remove_reference_t<_ExPo>::_Ivdep) { +#pragma loop(ivdep) + for (; _Count > 0; --_Count, (void) ++_UFirst) { + _STD _Construct_in_place(*_UFirst); + } + _STD _Seek_wrapped(_First, _UFirst); + } else { + for (; _Count > 0; --_Count, (void) ++_UFirst) { + _STD _Construct_in_place(*_UFirst); + } + _STD _Seek_wrapped(_First, _UFirst); + } + + return _First; +} _STD_END #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS diff --git a/stl/inc/memory b/stl/inc/memory index 655f32c66ee..001397df8c0 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -26,6 +26,24 @@ _STL_DISABLE_CLANG_WARNINGS #undef new _STD_BEGIN +#if _HAS_CXX17 +#define _REQUIRE_PARALLEL_LVALUE_ITERATOR(_Iter) \ + static_assert(_Is_ranges_fwd_iter_v<_Iter> && is_lvalue_reference_v<_Iter_ref_t<_Iter>>, \ + "Parallel specialized algorithms require the iterator type to be forward iterator and dereference " \ + "to lvalues.") + +_EXPORT_STD template = 0> +_NoThrowFwdIt uninitialized_copy(_ExPo&&, const _FwdIt _First, const _FwdIt _Last, _NoThrowFwdIt _Dest) noexcept +/* terminates */ { + // copy [_First, _Last) to raw [_Dest, ...) + // not parallelized at present + _REQUIRE_PARALLEL_ITERATOR(_FwdIt); + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + + return _STD uninitialized_copy(_First, _Last, _Dest); +} +#endif // _HAS_CXX17 + #ifdef __cpp_lib_concepts namespace ranges { _EXPORT_STD template @@ -129,6 +147,21 @@ _NoThrowFwdIt uninitialized_copy_n(const _InIt _First, const _Diff _Count_raw, _ return _Dest; } +#if _HAS_CXX17 +_EXPORT_STD template = 0> +_NoThrowFwdIt uninitialized_copy_n(_ExPo&&, const _FwdIt _First, const _Diff _Count_raw, _NoThrowFwdIt _Dest) noexcept +/* terminates */ { + // copy [_First, _First + _Count) to raw [_Dest, ...) + // not parallelized at present + _REQUIRE_PARALLEL_ITERATOR(_FwdIt); + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + + _Algorithm_int_t<_Diff> _Count = _Count_raw; + return _STD uninitialized_copy_n(_First, _Count, _Dest); +} +#endif // _HAS_CXX17 + #ifdef __cpp_lib_concepts namespace ranges { _EXPORT_STD template @@ -194,6 +227,17 @@ _NoThrowFwdIt uninitialized_move(const _InIt _First, const _InIt _Last, _NoThrow return _Dest; } +_EXPORT_STD template = 0> +_NoThrowFwdIt uninitialized_move(_ExPo&&, const _FwdIt _First, const _FwdIt _Last, _NoThrowFwdIt _Dest) noexcept +/* terminates */ { + // move [_First, _Last) to raw [_Dest, ...) + // not parallelized at present + _REQUIRE_PARALLEL_ITERATOR(_FwdIt); + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + + return _STD uninitialized_move(_First, _Last, _Dest); +} + #ifdef __cpp_lib_concepts namespace ranges { class _Uninitialized_move_fn : private _Not_quite_object { @@ -259,8 +303,30 @@ pair<_InIt, _NoThrowFwdIt> uninitialized_move_n(_InIt _First, const _Diff _Count _Seek_wrapped(_First, _UFirst); return {_First, _Dest}; } -#endif // _HAS_CXX17 +_EXPORT_STD template = 0> +pair<_FwdIt, _NoThrowFwdIt> uninitialized_move_n( + _ExPo&&, const _FwdIt _First, const _Diff _Count_raw, _NoThrowFwdIt _Dest) noexcept /* terminates */ { + // move [_First, _First + _Count) to raw [_Dest, ...) + // not parallelized at present + _REQUIRE_PARALLEL_ITERATOR(_FwdIt); + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + + _Algorithm_int_t<_Diff> _Count = _Count_raw; + return _STD uninitialized_move_n(_First, _Count, _Dest); +} + +_EXPORT_STD template = 0> +void uninitialized_fill(_ExPo&&, const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, const _Tval& _Val) noexcept +/* terminates */ { + // copy _Val throughout raw [_First, _Last) + // not parallelized at present + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + + _STD uninitialized_fill(_First, _Last, _Val); +} +#endif // _HAS_CXX17 #ifdef __cpp_lib_concepts namespace ranges { @@ -403,6 +469,21 @@ _NoThrowFwdIt uninitialized_fill_n(_NoThrowFwdIt _First, const _Diff _Count_raw, return _First; } +#if _HAS_CXX17 +_EXPORT_STD template = 0> +_NoThrowFwdIt uninitialized_fill_n( + _ExPo&&, const _NoThrowFwdIt _First, const _Diff _Count_raw, const _Tval& _Val) noexcept +/* terminates */ { + // copy _Count copies of _Val to raw _First + // not parallelized at present + _REQUIRE_PARALLEL_LVALUE_ITERATOR(_NoThrowFwdIt); + + _Algorithm_int_t<_Diff> _Count = _Count_raw; + return _STD uninitialized_fill_n(_First, _Count, _Val); +} +#endif // _HAS_CXX17 + #ifdef __cpp_lib_concepts namespace ranges { class _Uninitialized_fill_n_fn : private _Not_quite_object { @@ -493,6 +574,9 @@ _CONSTEXPR20 void destroy(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) _Destroy_range(_Get_unwrapped(_First), _Get_unwrapped(_Last)); } +_EXPORT_STD template = 0> +void destroy(_ExPo&& _Exec, _NoThrowFwdIt _First, _NoThrowFwdIt _Last) noexcept; // terminates + #ifdef __cpp_lib_concepts namespace ranges { template <_No_throw_input_iterator _It, _No_throw_sentinel_for<_It> _Se> @@ -557,6 +641,9 @@ _CONSTEXPR20 _NoThrowFwdIt destroy_n(_NoThrowFwdIt _First, const _Diff _Count_ra return _First; } +_EXPORT_STD template = 0> +_NoThrowFwdIt destroy_n(_ExPo&& _Exec, _NoThrowFwdIt _First, _Diff _Count_raw) noexcept; // terminates + #ifdef __cpp_lib_concepts namespace ranges { class _Destroy_n_fn : private _Not_quite_object { @@ -607,6 +694,9 @@ void uninitialized_default_construct(const _NoThrowFwdIt _First, const _NoThrowF } } +_EXPORT_STD template = 0> +void uninitialized_default_construct(_ExPo&& _Exec, _NoThrowFwdIt _First, _NoThrowFwdIt _Last) noexcept; // terminates + #ifdef __cpp_lib_concepts namespace ranges { class _Uninitialized_default_construct_fn : private _Not_quite_object { @@ -683,6 +773,10 @@ _NoThrowFwdIt uninitialized_default_construct_n(_NoThrowFwdIt _First, const _Dif return _First; } +_EXPORT_STD template = 0> +_NoThrowFwdIt uninitialized_default_construct_n( + _ExPo&& _Exec, _NoThrowFwdIt _First, _Diff _Count_raw) noexcept; // terminates + #ifdef __cpp_lib_concepts namespace ranges { class _Uninitialized_default_construct_n_fn : private _Not_quite_object { @@ -736,6 +830,9 @@ void uninitialized_value_construct(const _NoThrowFwdIt _First, const _NoThrowFwd } } +_EXPORT_STD template = 0> +void uninitialized_value_construct(_ExPo&& _Exec, _NoThrowFwdIt _First, _NoThrowFwdIt _Last) noexcept; // terminates + #ifdef __cpp_lib_concepts namespace ranges { class _Uninitialized_value_construct_fn : private _Not_quite_object { @@ -799,6 +896,10 @@ _NoThrowFwdIt uninitialized_value_construct_n(_NoThrowFwdIt _First, const _Diff return _First; } +_EXPORT_STD template = 0> +_NoThrowFwdIt uninitialized_value_construct_n( + _ExPo&& _Exec, _NoThrowFwdIt _First, const _Diff _Count_raw) noexcept; // terminates + #ifdef __cpp_lib_concepts namespace ranges { class _Uninitialized_value_construct_n_fn : private _Not_quite_object { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 541037dc0c8..6e788f5ec83 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -363,6 +363,8 @@ // * any_of // * count // * count_if +// * destroy +// * destroy_n // * equal // * exclusive_scan // * find @@ -396,6 +398,10 @@ // * transform_exclusive_scan // * transform_inclusive_scan // * transform_reduce +// * uninitialized_default_construct +// * uninitialized_default_construct_n +// * uninitialized_value_construct +// * uninitialized_value_construct_n // // The following are not presently parallelized: // @@ -414,6 +420,14 @@ // * shift_right // * swap_ranges // +// Possibly same as above, but not yet tested. +// * uninitialized_copy +// * uninitialized_copy_n +// * uninitialized_fill +// * uninitialized_fill_n +// * uninitialized_move +// * uninitialized_move_n +// // Confusion over user parallelism requirements exists; likely in the above category anyway. // * generate // * generate_n diff --git a/tests/std/test.lst b/tests/std/test.lst index 0a403a9eac3..208991c94f9 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -259,6 +259,7 @@ tests\P0024R2_parallel_algorithms_transform_inclusive_scan tests\P0024R2_parallel_algorithms_transform_reduce tests\P0035R4_over_aligned_allocation tests\P0040R3_extending_memory_management_tools +tests\P0040R3_parallel_memory_algorithms tests\P0053R7_cpp_synchronized_buffered_ostream tests\P0067R5_charconv tests\P0083R3_splicing_maps_and_sets diff --git a/tests/std/tests/P0040R3_parallel_memory_algorithms/env.lst b/tests/std/tests/P0040R3_parallel_memory_algorithms/env.lst new file mode 100644 index 00000000000..2de7aab2959 --- /dev/null +++ b/tests/std/tests/P0040R3_parallel_memory_algorithms/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_17_matrix.lst diff --git a/tests/std/tests/P0040R3_parallel_memory_algorithms/test.cpp b/tests/std/tests/P0040R3_parallel_memory_algorithms/test.cpp new file mode 100644 index 00000000000..abeb44503aa --- /dev/null +++ b/tests/std/tests/P0040R3_parallel_memory_algorithms/test.cpp @@ -0,0 +1,450 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace std::execution; + +class wrap_uchar { +private: + unsigned char ch_ = 42; + +public: + bool is_expected() const noexcept { + return ch_ == 42; + } +}; + +static_assert(sizeof(wrap_uchar) == 1); + +const auto expectation = [](const wrap_uchar& x) { return x.is_expected(); }; + +const auto expectation_zero = [](int n) { return n == 0; }; + +constexpr auto bad_uchar = static_cast(0xcd); +constexpr auto bad_int = static_cast(0xdeadbeef); + +struct resetting_guard { + int* ptr_ = nullptr; + + resetting_guard() = default; + + ~resetting_guard() { + if (ptr_) { + *ptr_ = 0; + } + } + + resetting_guard(const resetting_guard&) = delete; + resetting_guard& operator=(const resetting_guard&) = delete; +}; + +template +struct deallocating_only_deleter { + size_t count_ = 0; + + void operator()(T* ptr) const noexcept { + allocator{}.deallocate(ptr, count_); + } +}; + +template +unique_ptr> make_constructed_nondestroying_buffer(size_t n) { + if (n == 0) { + return unique_ptr>{}; + } + + allocator al; + unique_ptr> up{al.allocate(n), deallocating_only_deleter{n}}; + for (size_t i = 0; i != n; ++i) { + allocator_traits>::construct(al, up.get() + i); + } + + return up; +} + +template +unique_ptr> make_unconstructed_nondestroying_buffer(size_t n) { + if (n == 0) { + return unique_ptr>{}; + } else { + return unique_ptr>{allocator{}.allocate(n), deallocating_only_deleter{n}}; + } +} + +struct test_case_uninitialized_default_construct_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + const auto begin_it = reinterpret_cast(buffer.get()); + const auto end_it = begin_it + testSize; + + fill_n(buffer.get(), testSize, bad_uchar); + + uninitialized_default_construct(exec, begin_it, end_it); + assert(all_of(begin_it, end_it, expectation)); + } +}; + +struct test_case_uninitialized_default_construct_n_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + const auto begin_it = reinterpret_cast(buffer.get()); + const auto end_it = begin_it + testSize; + + fill_n(buffer.get(), testSize, bad_uchar); + + const auto result_it = uninitialized_default_construct_n(exec, begin_it, testSize); + assert(all_of(begin_it, end_it, expectation)); + assert(end_it == result_it); + } +}; + +struct test_case_uninitialized_default_construct_trivial_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unconstructed_nondestroying_buffer(testSize); + const auto begin_it = buffer.get(); + const auto end_it = begin_it + testSize; + + uninitialized_default_construct(exec, begin_it, end_it); + } +}; + +struct test_case_uninitialized_default_construct_n_trivial_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unconstructed_nondestroying_buffer(testSize); + const auto begin_it = buffer.get(); + const auto end_it = begin_it + testSize; + + const auto result_it = uninitialized_default_construct_n(exec, begin_it, testSize); + assert(end_it == result_it); + } +}; + +struct test_case_uninitialized_value_construct_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + const auto begin_it = reinterpret_cast(buffer.get()); + const auto end_it = begin_it + testSize; + + fill_n(buffer.get(), testSize, bad_uchar); + + uninitialized_value_construct(exec, begin_it, end_it); + assert(all_of(begin_it, end_it, expectation)); + } +}; + +struct test_case_uninitialized_value_construct_n_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + const auto begin_it = reinterpret_cast(buffer.get()); + const auto end_it = begin_it + testSize; + + fill_n(buffer.get(), testSize, bad_uchar); + + const auto result_it = uninitialized_value_construct_n(exec, begin_it, testSize); + assert(all_of(begin_it, end_it, expectation)); + assert(end_it == result_it); + } +}; + +struct test_case_uninitialized_value_construct_memset_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + const auto begin_it = buffer.get(); + const auto end_it = begin_it + testSize; + + fill_n(begin_it, testSize, bad_int); + + uninitialized_value_construct(exec, begin_it, end_it); + assert(all_of(begin_it, end_it, expectation_zero)); + } +}; + +struct test_case_uninitialized_value_construct_n_memset_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + const auto begin_it = buffer.get(); + const auto end_it = begin_it + testSize; + + fill_n(begin_it, testSize, bad_int); + + const auto result_it = uninitialized_value_construct_n(exec, begin_it, testSize); + assert(all_of(begin_it, end_it, expectation_zero)); + assert(end_it == result_it); + } +}; + +struct test_case_uninitialized_value_construct_unwrap_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto vec = vector(testSize, bad_int); + const auto begin_it = vec.begin(); + const auto end_it = vec.end(); + + uninitialized_value_construct(exec, begin_it, end_it); + assert(all_of(begin_it, end_it, expectation_zero)); + } +}; + +struct test_case_uninitialized_value_construct_n_unwrap_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto vec = vector(testSize, bad_int); + const auto begin_it = vec.begin(); + const auto end_it = vec.end(); + + const auto result_it = uninitialized_value_construct_n(exec, begin_it, testSize); + assert(all_of(begin_it, end_it, expectation_zero)); + assert(end_it == result_it); + } +}; + +struct test_case_destroy_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + const auto begin_it = reinterpret_cast(buffer.get()); + const auto end_it = begin_it + testSize; + + fill_n(buffer.get(), testSize, bad_uchar); + + uninitialized_default_construct(exec, begin_it, end_it); + destroy(exec, begin_it, end_it); + } +}; + +struct test_case_destroy_n_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + const auto begin_it = reinterpret_cast(buffer.get()); + const auto end_it = begin_it + testSize; + + fill_n(buffer.get(), testSize, bad_uchar); + + uninitialized_default_construct_n(begin_it, testSize); + const auto result_it = destroy_n(exec, begin_it, testSize); + assert(end_it == result_it); + } +}; + +struct test_case_destroy_nontrivial_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer_to_destroy = make_constructed_nondestroying_buffer(testSize); + const auto begin_it = buffer_to_destroy.get(); + const auto end_it = begin_it + testSize; + + auto buffer_to_clear = make_unique(testSize); + const auto begin_it_validation = buffer_to_clear.get(); + const auto end_it_validation = begin_it_validation + testSize; + + fill(begin_it_validation, end_it_validation, bad_int); + auto it_guard = begin_it; + for (auto it_int = begin_it_validation; it_int != end_it_validation; ++it_int) { + it_guard->ptr_ = it_int; + ++it_guard; + } + + destroy(exec, begin_it, end_it); + assert(all_of(begin_it_validation, end_it_validation, expectation_zero)); + } +}; + +struct test_case_destroy_n_nontrivial_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer_to_destroy = make_constructed_nondestroying_buffer(testSize); + const auto begin_it = buffer_to_destroy.get(); + const auto end_it = begin_it + testSize; + + auto buffer_to_clear = make_unique(testSize); + const auto begin_it_validation = buffer_to_clear.get(); + const auto end_it_validation = begin_it_validation + testSize; + + fill_n(begin_it_validation, testSize, bad_int); + auto it_guard = begin_it; + for (auto it_int = begin_it_validation; it_int != end_it_validation; ++it_int) { + it_guard->ptr_ = it_int; + ++it_guard; + } + + const auto result_it = destroy_n(exec, begin_it, testSize); + assert(end_it == result_it); + assert(all_of(begin_it_validation, end_it_validation, expectation_zero)); + } +}; + +struct test_case_uninitialized_copy_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + auto buffer2 = make_unique(testSize); + const auto begin_it = buffer.get(); + const auto end_it = begin_it + testSize; + + fill_n(buffer2.get(), testSize, bad_int); + iota(begin_it, end_it, 42); + + const auto begin_it2 = buffer2.get(); + const auto end_it2 = begin_it2 + testSize; + + uninitialized_copy(exec, begin_it, end_it, begin_it2); + assert(equal(begin_it, end_it, begin_it2, end_it2)); + } +}; + +struct test_case_uninitialized_copy_n_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + auto buffer2 = make_unique(testSize); + const auto begin_it = buffer.get(); + const auto end_it = begin_it + testSize; + + fill_n(buffer2.get(), testSize, bad_int); + iota(begin_it, end_it, 42); + + const auto begin_it2 = buffer2.get(); + const auto end_it2 = begin_it2 + testSize; + + const auto result_it = uninitialized_copy_n(exec, begin_it, testSize, begin_it2); + assert(equal(begin_it, end_it, begin_it2, end_it2)); + assert(end_it2 == result_it); + } +}; + +struct test_case_uninitialized_move_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + auto buffer2 = make_unique(testSize); + const auto begin_it = buffer.get(); + const auto end_it = begin_it + testSize; + + fill_n(buffer2.get(), testSize, bad_int); + iota(begin_it, end_it, 42); + + const auto begin_it2 = buffer2.get(); + const auto end_it2 = begin_it2 + testSize; + + uninitialized_move(exec, begin_it, end_it, begin_it2); + assert(equal(begin_it, end_it, begin_it2, end_it2)); + } +}; + +struct test_case_uninitialized_move_n_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + auto buffer2 = make_unique(testSize); + const auto begin_it = buffer.get(); + const auto end_it = begin_it + testSize; + + fill_n(buffer2.get(), testSize, bad_int); + iota(begin_it, end_it, 42); + + const auto begin_it2 = buffer2.get(); + const auto end_it2 = begin_it2 + testSize; + + const auto result_pair = uninitialized_move_n(exec, begin_it, testSize, begin_it2); + assert(equal(begin_it, end_it, begin_it2, end_it2)); + assert(end_it == result_pair.first && end_it2 == result_pair.second); + } +}; + +struct test_case_uninitialized_fill_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + const auto begin_it = buffer.get(); + const auto end_it = begin_it + testSize; + + fill(begin_it, end_it, bad_int); + + uninitialized_fill(exec, begin_it, end_it, 42); + assert(all_of(begin_it, end_it, [](int n) { return n == 42; })); + } +}; + +struct test_case_uninitialized_fill_n_parallel { + template + void operator()(const size_t testSize, const ExecutionPolicy& exec) { + auto buffer = make_unique(testSize); + const auto begin_it = buffer.get(); + const auto end_it = begin_it + testSize; + + fill_n(begin_it, testSize, bad_int); + + const auto result_it = uninitialized_fill_n(exec, begin_it, testSize, 42); + assert(all_of(begin_it, end_it, [](int n) { return n == 42; })); + assert(end_it == result_it); + } +}; + +int main() { +#ifndef _M_CEE // TRANSITION, VSO-1664463 + parallel_test_case(test_case_uninitialized_default_construct_parallel{}, par); + parallel_test_case(test_case_uninitialized_default_construct_n_parallel{}, par); + parallel_test_case(test_case_uninitialized_default_construct_trivial_parallel{}, par); + parallel_test_case(test_case_uninitialized_default_construct_n_trivial_parallel{}, par); + parallel_test_case(test_case_uninitialized_value_construct_parallel{}, par); + parallel_test_case(test_case_uninitialized_value_construct_n_parallel{}, par); + parallel_test_case(test_case_uninitialized_value_construct_memset_parallel{}, par); + parallel_test_case(test_case_uninitialized_value_construct_n_memset_parallel{}, par); + parallel_test_case(test_case_uninitialized_value_construct_unwrap_parallel{}, par); + parallel_test_case(test_case_uninitialized_value_construct_n_unwrap_parallel{}, par); + parallel_test_case(test_case_destroy_parallel{}, par); + parallel_test_case(test_case_destroy_n_parallel{}, par); + parallel_test_case(test_case_destroy_nontrivial_parallel{}, par); + parallel_test_case(test_case_destroy_n_nontrivial_parallel{}, par); + + // currently not parallelized + parallel_test_case(test_case_uninitialized_copy_parallel{}, par); + parallel_test_case(test_case_uninitialized_copy_n_parallel{}, par); + parallel_test_case(test_case_uninitialized_move_parallel{}, par); + parallel_test_case(test_case_uninitialized_move_n_parallel{}, par); + parallel_test_case(test_case_uninitialized_fill_parallel{}, par); + parallel_test_case(test_case_uninitialized_fill_n_parallel{}, par); +#if _HAS_CXX20 + parallel_test_case(test_case_uninitialized_default_construct_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_default_construct_n_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_default_construct_trivial_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_default_construct_n_trivial_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_value_construct_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_value_construct_n_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_value_construct_memset_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_value_construct_n_memset_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_value_construct_unwrap_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_value_construct_n_unwrap_parallel{}, unseq); + parallel_test_case(test_case_destroy_parallel{}, unseq); + parallel_test_case(test_case_destroy_n_parallel{}, unseq); + parallel_test_case(test_case_destroy_nontrivial_parallel{}, unseq); + parallel_test_case(test_case_destroy_n_nontrivial_parallel{}, unseq); + + // currently not parallelized + parallel_test_case(test_case_uninitialized_copy_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_copy_n_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_move_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_move_n_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_fill_parallel{}, unseq); + parallel_test_case(test_case_uninitialized_fill_n_parallel{}, unseq); +#endif // _HAS_CXX20 +#endif // _M_CEE +}