diff --git a/cmake/HPX_Documentation.cmake b/cmake/HPX_Documentation.cmake index aaac29a32c32..fe9b38887fcb 100644 --- a/cmake/HPX_Documentation.cmake +++ b/cmake/HPX_Documentation.cmake @@ -5,6 +5,7 @@ # Distributed under the Boost Software License, Version 1.0. (See accompanying # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# find required packages if(HPX_WITH_DOCUMENTATION) find_package(Doxygen) find_package(Sphinx) @@ -27,22 +28,3 @@ if(HPX_WITH_DOCUMENTATION) set(HPX_WITH_DOCUMENTATION OFF) endif() endif() - -# C++ Source -> Doxygen XML -function(hpx_source_to_doxygen name) - set(options) - set(one_value_args) - set(multi_value_args DEPENDENCIES DOXYGEN_ARGS) - cmake_parse_arguments( - ${name} "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN} - ) - - add_custom_command( - OUTPUT "${name}/index.xml" - COMMAND "${DOXYGEN_EXECUTABLE}" ${${name}_DOXYGEN_ARGS} - "${CMAKE_CURRENT_BINARY_DIR}/${name}.doxy" - COMMENT "Generating Doxygen." - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${name}.doxy" ${${name}_DEPENDENCIES} - VERBATIM - ) -endfunction() diff --git a/cmake/templates/autodoc.doxy.in b/cmake/templates/autodoc.doxy.in deleted file mode 100644 index 94113e0f146d..000000000000 --- a/cmake/templates/autodoc.doxy.in +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2012 Bryce Adelstein-Lelbach -# -# SPDX-License-Identifier: BSL-1.0 -# Distributed under the Boost Software License, Version 1.0. (See accompanying -# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -EXTRACT_ALL = YES -EXPAND_ONLY_PREDEF = YES -GENERATE_HTML = NO -GENERATE_XML = YES -MACRO_EXPANSION = YES -PREDEFINED = @doxygen_definitions@ -XML_OUTPUT = @doxygen_output_file@ -OUTPUT_DIRECTORY = @doxygen_output_dir@ -GENERATE_LATEX = NO -INPUT = @doxygen_inputs@ -EXCLUDE_SYMBOLS = detail -EXTRACT_PRIVATE = NO diff --git a/cmake/templates/conf.py.in b/cmake/templates/conf.py.in index 65ab8d053d5c..4f004259e689 100644 --- a/cmake/templates/conf.py.in +++ b/cmake/templates/conf.py.in @@ -208,8 +208,13 @@ breathe_doxygen_config_options = { 'PREDEFINED': '@doxygen_definitions@', 'STRIP_FROM_PATH': hpx_source_dir, 'EXTRACT_PRIVATE': 'NO' + 'ALIASES': r'namedrequirement{1}="\1"' } +# ! this should work but doesn't. check after updating breathe version. +# breathe_doxygen_aliases = { +# } + numfig = True primary_domain = 'cpp' highlight_language = 'cpp' @@ -496,6 +501,10 @@ rst_prolog += ''' .. _cpp20_p0075r1: http://wg21.link/p0075r1 .. |cpp20_p0443| replace:: P0443 .. _cpp20_p0443: https://github.com/executors/issaquah_2016 +.. |p0792| replace:: P0792 +.. _p0792: http://wg21.link/p0792 +.. |p0159| replace:: P0159 +.. _p0159: http://wg21.link/p0159 .. |p1393| replace:: P1393 .. _p1393: http://wg21.link/p1393 .. |p2220| replace:: P2220 diff --git a/cmake/templates/hpxcxx.in b/cmake/templates/hpxcxx.in index 81f38f89367d..9a0196e0d4eb 100755 --- a/cmake/templates/hpxcxx.in +++ b/cmake/templates/hpxcxx.in @@ -126,12 +126,14 @@ if pkg in os.environ: else: os.environ[pkg] = pkgconf +args += ["-Wl,--start-group"] if application: args += ["`pkg-config --cflags --libs hpx_application" + pkgconf_suffix + "`"] elif component: args += ["`pkg-config --cflags --libs hpx_component" + pkgconf_suffix + "`"] else: args += ["`pkg-config --cflags hpx_application" + pkgconf_suffix + "`"] +args += ["-Wl,--end-group"] if not component and not application and not minusc: usage() diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index b2b06a9347a9..437aa6671b00 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -4,20 +4,7 @@ # Distributed under the Boost Software License, Version 1.0. (See accompanying # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -# Add all files here which should be passed to doxygen -set(doxygen_dependencies - "${PROJECT_SOURCE_DIR}/components/component_storage/include/hpx/components/component_storage/migrate_from_storage.hpp" - "${PROJECT_SOURCE_DIR}/components/component_storage/include/hpx/components/component_storage/migrate_to_storage.hpp" -) - -# Definitions for autodoc.doxy.in -foreach(doxygen_input ${doxygen_dependencies}) - set(doxygen_inputs "${doxygen_inputs} ${doxygen_input}") -endforeach() - -set(doxygen_output_file "${CMAKE_CURRENT_BINARY_DIR}/hpx_autodoc") -set(doxygen_output_dir "${CMAKE_CURRENT_BINARY_DIR}/doxygen") - +# used while generating sphinx config file set(doxygen_definition_list "DOXYGEN:=1" "BOOST_SYSTEM_NOEXCEPT=" @@ -36,12 +23,6 @@ foreach(doxygen_predef ${doxygen_definition_list}) set(doxygen_definitions "${doxygen_definitions} \"${doxygen_predef}\"") endforeach() -hpx_info("Creating Doxyfile: ${CMAKE_CURRENT_BINARY_DIR}/hpx_autodoc.doxy") -configure_file( - "${PROJECT_SOURCE_DIR}/cmake/templates/autodoc.doxy.in" - "${CMAKE_CURRENT_BINARY_DIR}/hpx_autodoc.doxy" @ONLY -) - # Generate rst files with CMake variables and toolchains set(HPX_CMAKE_OPTIONS_RST "") foreach(_cat ${HPX_OPTION_CATEGORIES}) @@ -215,8 +196,6 @@ create_symbolic_link( "${PROJECT_SOURCE_DIR}/libs" "${CMAKE_CURRENT_BINARY_DIR}/libs" ) -hpx_source_to_doxygen(hpx_autodoc DEPENDENCIES ${doxygen_dependencies}) - add_custom_target(docs) add_custom_target(docs-html) add_custom_target(docs-singlehtml) @@ -258,7 +237,6 @@ foreach(output_format ${HPX_WITH_DOCUMENTATION_OUTPUT_FORMATS}) add_custom_command( OUTPUT "${SPHINX_DOCS_OUTPUT_FILE}" DEPENDS "${sphinx_source_files_build}" - "${CMAKE_CURRENT_BINARY_DIR}/hpx_autodoc/index.xml" COMMAND ${SPHINX_DOCS_BUILD_COMMAND} ) diff --git a/docs/sphinx/api/public_api.rst b/docs/sphinx/api/public_api.rst index 400d0c008ec7..e4cd170e5e07 100644 --- a/docs/sphinx/api/public_api.rst +++ b/docs/sphinx/api/public_api.rst @@ -459,7 +459,7 @@ Classes Class C++ standard ============================================= ============================================================= :cpp:class:`hpx::function` :cppreference-generic:`utility/functional,function` - :cpp:class:`hpx::function_ref` + :cpp:class:`hpx::function_ref` |p0792|_ :cpp:class:`hpx::move_only_function` :cppreference-generic:`utility/functional,move_only_function` :cpp:struct:`hpx::traits::is_bind_expression` :cppreference-generic:`utility/functional,is_bind_expression` :cpp:struct:`hpx::traits::is_placeholder` :cppreference-generic:`utility/functional,is_placeholder` @@ -541,13 +541,13 @@ Functions :cpp:func:`hpx::dataflow` :cpp:func:`hpx::make_future` :cpp:func:`hpx::make_shared_future` - :cpp:func:`hpx::make_ready_future` + :cpp:func:`hpx::make_ready_future` |p0159|_ :cpp:func:`hpx::make_ready_future_alloc` :cpp:func:`hpx::make_ready_future_at` :cpp:func:`hpx::make_ready_future_after` - :cpp:func:`hpx::make_exceptional_future` - :cpp:func:`hpx::when_all` - :cpp:func:`hpx::when_any` + :cpp:func:`hpx::make_exceptional_future` |p0159|_ + :cpp:func:`hpx::when_all` |p0159|_ + :cpp:func:`hpx::when_any` |p0159|_ :cpp:func:`hpx::when_some` :cpp:func:`hpx::when_each` :cpp:func:`hpx::wait_all` diff --git a/docs/sphinx/manual/building_hpx.rst b/docs/sphinx/manual/building_hpx.rst index 4df46bdae228..ccc33a9560be 100644 --- a/docs/sphinx/manual/building_hpx.rst +++ b/docs/sphinx/manual/building_hpx.rst @@ -253,14 +253,14 @@ To build |hpx| under Windows 10 x64 with Visual Studio 2015: .. code-block:: bash - bootstrap.bat + .\bootstrap.bat This batch file will set up everything needed to create a successful build. Now execute: .. code-block:: bash - b2.exe link=shared variant=release,debug architecture=x86 address-model=64 threading=multi --build-type=complete install + .\b2.exe link=shared variant=release,debug architecture=x86 address-model=64 threading=multi --build-type=complete install This command will start a (very long) build of all available Boost libraries. Please, be patient. @@ -285,12 +285,14 @@ To build |hpx| under Windows 10 x64 with Visual Studio 2015: will build |hpx| packages out of the |hpx| source tree. * Set three new environment variables (in CMake, not in Windows environment): - ``BOOST_ROOT``, ``HWLOC_ROOT``, ``CMAKE_INSTALL_PREFIX``. The meaning of + ``BOOST_ROOT``, ``HWLOC_ROOT``, ``ASIO_ROOT``, ``CMAKE_INSTALL_PREFIX``. The meaning of these variables is as follows: * ``BOOST_ROOT`` the |hpx| root directory of the unpacked Boost headers/cpp files. * ``HWLOC_ROOT`` the |hpx| root directory of the unpacked Portable Hardware Locality files. + * ``ASIO_ROOT`` the |hpx| root directory of the unpacked ASIO files. Alternatively use + ``HPX_WITH_FETCH_ASIO`` with value ``True``. * ``CMAKE_INSTALL_PREFIX`` the |hpx| root directory where the future builds of |hpx| should be installed. diff --git a/docs/sphinx/manual/launching_and_configuring_hpx_applications.rst b/docs/sphinx/manual/launching_and_configuring_hpx_applications.rst index 01de5595f226..1151c731fade 100644 --- a/docs/sphinx/manual/launching_and_configuring_hpx_applications.rst +++ b/docs/sphinx/manual/launching_and_configuring_hpx_applications.rst @@ -1461,6 +1461,13 @@ The predefined command line options for any application using Sed-style search and replace (``s/search/replace/``) used to transform host names to the proper network interconnect. +.. option:: --hpx:force_ipv4 + + Network hostnames will be resolved to ipv4 adresses instead of using the + first resolved endpoint. This is especially useful on Windows where the + local hostname will resolve to an ipv6 adress while remote network hostnames + are commonly resolved to ipv4 adresses. + .. option:: --hpx:localities arg The number of localities to wait for at application startup (default: ``1``). diff --git a/libs/core/algorithms/CMakeLists.txt b/libs/core/algorithms/CMakeLists.txt index 573e3d74035f..260dbd7f5505 100644 --- a/libs/core/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/CMakeLists.txt @@ -35,6 +35,7 @@ set(algorithms_headers hpx/parallel/algorithms/detail/parallel_stable_sort.hpp hpx/parallel/algorithms/detail/pivot.hpp hpx/parallel/algorithms/detail/reduce.hpp + hpx/parallel/algorithms/detail/replace.hpp hpx/parallel/algorithms/detail/rotate.hpp hpx/parallel/algorithms/detail/sample_sort.hpp hpx/parallel/algorithms/detail/search.hpp @@ -168,6 +169,7 @@ set(algorithms_headers hpx/parallel/datapar/loop.hpp hpx/parallel/datapar/mismatch.hpp hpx/parallel/datapar/reduce.hpp + hpx/parallel/datapar/replace.hpp hpx/parallel/datapar/transfer.hpp hpx/parallel/datapar/transform_loop.hpp hpx/parallel/datapar/zip_iterator.hpp diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/detail/replace.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/detail/replace.hpp new file mode 100644 index 000000000000..246bbb72f426 --- /dev/null +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/detail/replace.hpp @@ -0,0 +1,265 @@ +// Copyright (c) 2022 Srinivas Yadav +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace hpx { namespace parallel { inline namespace v1 { namespace detail { + + /////////////////////////////////////////////////////////////////////////// + template + struct sequential_replace_t final + : hpx::functional::detail::tag_fallback> + { + private: + template + friend inline constexpr auto tag_fallback_invoke(sequential_replace_t, + ExPolicy&& policy, InIter first, InIter last, T1 const& old_value, + T2 const& new_value, Proj&& proj) + { + if constexpr (hpx::is_sequenced_execution_policy_v) + { + return util::loop(HPX_FORWARD(ExPolicy, policy), first, last, + [old_value, new_value, &proj](auto& v) { + if (HPX_INVOKE(proj, *v) == old_value) + { + *v = new_value; + } + }); + } + else + { + typedef typename std::iterator_traits::value_type type; + + return for_each_n().call( + HPX_FORWARD(ExPolicy, policy), first, + std::distance(first, last), + [old_value, new_value, proj = HPX_FORWARD(Proj, proj)]( + type& t) -> void { + if (HPX_INVOKE(proj, t) == old_value) + { + t = new_value; + } + }, + util::projection_identity()); + } + } + }; + +#if !defined(HPX_COMPUTE_DEVICE_CODE) + template + inline constexpr sequential_replace_t sequential_replace = + sequential_replace_t{}; +#else + template + HPX_HOST_DEVICE HPX_FORCEINLINE auto sequential_replace(Args&&... args) + { + return sequential_replace_t{}(std::forward(args)...); + } +#endif + + /////////////////////////////////////////////////////////////////////////// + template + struct sequential_replace_if_t final + : hpx::functional::detail::tag_fallback> + { + private: + template + friend inline constexpr auto tag_fallback_invoke( + sequential_replace_if_t, ExPolicy&& policy, InIter first, Sent last, + F&& f, T const& new_value, Proj&& proj) + { + if constexpr (hpx::is_sequenced_execution_policy_v) + { + return util::loop(HPX_FORWARD(ExPolicy, policy), first, last, + [&f, new_value, &proj](auto& v) { + if (HPX_INVOKE(f, HPX_INVOKE(proj, *v))) + { + *v = new_value; + } + }); + } + else + { + typedef typename std::iterator_traits::value_type type; + + return for_each_n().call( + HPX_FORWARD(ExPolicy, policy), first, + detail::distance(first, last), + [new_value, f = HPX_FORWARD(F, f), + proj = HPX_FORWARD(Proj, proj)]( + type& t) mutable -> void { + if (HPX_INVOKE(f, HPX_INVOKE(proj, t))) + { + t = new_value; + } + }, + util::projection_identity()); + } + } + }; + +#if !defined(HPX_COMPUTE_DEVICE_CODE) + template + inline constexpr sequential_replace_if_t sequential_replace_if = + sequential_replace_if_t{}; +#else + template + HPX_HOST_DEVICE HPX_FORCEINLINE auto sequential_replace_if(Args&&... args) + { + return sequential_replace_if_t{}(std::forward(args)...); + } +#endif + + /////////////////////////////////////////////////////////////////////////// + template + struct sequential_replace_copy_t final + : hpx::functional::detail::tag_fallback< + sequential_replace_copy_t> + { + private: + template + friend inline constexpr auto tag_fallback_invoke( + sequential_replace_copy_t, ExPolicy&& policy, InIter first, + Sent sent, OutIter dest, T const& old_value, T const& new_value, + Proj&& proj) + { + if constexpr (hpx::is_sequenced_execution_policy_v) + { + for (/* */; first != sent; ++first) + { + if (HPX_INVOKE(proj, *first) == old_value) + *dest++ = new_value; + else + *dest++ = *first; + } + return util::in_out_result(first, dest); + } + else + { + typedef hpx::util::zip_iterator zip_iterator; + typedef typename zip_iterator::reference reference; + + return util::detail::get_in_out_result( + for_each_n().call( + HPX_FORWARD(ExPolicy, policy), + hpx::util::make_zip_iterator(first, dest), + detail::distance(first, sent), + [old_value, new_value, proj = HPX_FORWARD(Proj, proj)]( + reference t) -> void { + using hpx::get; + if (HPX_INVOKE(proj, get<0>(t)) == old_value) + get<1>(t) = new_value; + else + get<1>(t) = get<0>(t); //-V573 + }, + util::projection_identity())); + } + } + }; + +#if !defined(HPX_COMPUTE_DEVICE_CODE) + template + inline constexpr sequential_replace_copy_t + sequential_replace_copy = sequential_replace_copy_t{}; +#else + template + HPX_HOST_DEVICE HPX_FORCEINLINE auto sequential_replace_copy(Args&&... args) + { + return sequential_replace_copy_t{}( + std::forward(args)...); + } +#endif + + /////////////////////////////////////////////////////////////////////////// + template + struct sequential_replace_copy_if_t final + : hpx::functional::detail::tag_fallback< + sequential_replace_copy_if_t> + { + private: + template + friend inline constexpr auto tag_fallback_invoke( + sequential_replace_copy_if_t, ExPolicy&& policy, InIter first, + Sent sent, OutIter dest, F&& f, T const& new_value, Proj&& proj) + { + if constexpr (hpx::is_sequenced_execution_policy_v) + { + for (/* */; first != sent; ++first) + { + if (HPX_INVOKE(f, HPX_INVOKE(proj, *first))) + { + *dest++ = new_value; + } + else + { + *dest++ = *first; + } + } + return util::in_out_result{first, dest}; + } + else + { + typedef hpx::util::zip_iterator zip_iterator; + typedef typename zip_iterator::reference reference; + + return util::detail::get_in_out_result( + for_each_n().call( + HPX_FORWARD(ExPolicy, policy), + hpx::util::make_zip_iterator(first, dest), + detail::distance(first, sent), + [new_value, f = HPX_FORWARD(F, f), + proj = HPX_FORWARD(Proj, proj)]( + reference t) mutable -> void { + using hpx::get; + if (HPX_INVOKE(f, HPX_INVOKE(proj, get<0>(t)))) + { + get<1>(t) = new_value; + } + else + { + get<1>(t) = get<0>(t); //-V573 + } + }, + util::projection_identity())); + } + } + }; + +#if !defined(HPX_COMPUTE_DEVICE_CODE) + template + inline constexpr sequential_replace_copy_if_t + sequential_replace_copy_if = sequential_replace_copy_if_t{}; +#else + template + HPX_HOST_DEVICE HPX_FORCEINLINE auto sequential_replace_copy_if( + Args&&... args) + { + return sequential_replace_copy_if_t{}( + std::forward(args)...); + } +#endif + +}}}} // namespace hpx::parallel::v1::detail diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp index 47d78e8a8444..04197914a9f5 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp @@ -481,6 +481,7 @@ namespace hpx { #include #include #include +#include #include #include #include @@ -497,21 +498,6 @@ namespace hpx { namespace parallel { inline namespace v1 { namespace detail { /// \cond NOINTERNAL - // sequential replace - template - inline InIter sequential_replace(InIter first, InIter last, - T1 const& old_value, T2 const& new_value, Proj&& proj) - { - for (/* */; first != last; ++first) - { - if (HPX_INVOKE(proj, *first) == old_value) - { - *first = new_value; - } - } - return first; - } - template struct replace : public detail::algorithm, Iter> { @@ -522,11 +508,13 @@ namespace hpx { namespace parallel { inline namespace v1 { template - static InIter sequential(ExPolicy, InIter first, InIter last, - T1 const& old_value, T2 const& new_value, Proj&& proj) + static InIter sequential(ExPolicy&& policy, InIter first, + InIter last, T1 const& old_value, T2 const& new_value, + Proj&& proj) { - return sequential_replace( - first, last, old_value, new_value, HPX_FORWARD(Proj, proj)); + return sequential_replace( + HPX_FORWARD(ExPolicy, policy), first, last, old_value, + new_value, HPX_FORWARD(Proj, proj)); } template ::value_type type; - - return for_each_n().call( - HPX_FORWARD(ExPolicy, policy), first, - std::distance(first, last), - [old_value, new_value, proj = HPX_FORWARD(Proj, proj)]( - type& t) -> void { - if (HPX_INVOKE(proj, t) == old_value) - { - t = new_value; - } - }, - util::projection_identity()); + return sequential_replace( + HPX_FORWARD(ExPolicy, policy), first, last, old_value, + new_value, HPX_FORWARD(Proj, proj)); } }; /// \endcond @@ -585,22 +563,6 @@ namespace hpx { namespace parallel { inline namespace v1 { namespace detail { /// \cond NOINTERNAL - // sequential replace_if - template - inline InIter sequential_replace_if( - InIter first, Sent sent, F&& f, T const& new_value, Proj&& proj) - { - for (/* */; first != sent; ++first) - { - if (HPX_INVOKE(f, HPX_INVOKE(proj, *first))) - { - *first = new_value; - } - } - return first; - } - template struct replace_if : public detail::algorithm, Iter> { @@ -611,11 +573,12 @@ namespace hpx { namespace parallel { inline namespace v1 { template - static InIter sequential(ExPolicy, InIter first, Sent last, F&& f, - T const& new_value, Proj&& proj) + static InIter sequential(ExPolicy&& policy, InIter first, Sent last, + F&& f, T const& new_value, Proj&& proj) { - return sequential_replace_if(first, last, HPX_FORWARD(F, f), - new_value, HPX_FORWARD(Proj, proj)); + return sequential_replace_if( + HPX_FORWARD(ExPolicy, policy), first, last, + HPX_FORWARD(F, f), new_value, HPX_FORWARD(Proj, proj)); } template ::value_type type; - - return for_each_n().call( - HPX_FORWARD(ExPolicy, policy), first, - detail::distance(first, last), - [new_value, f = HPX_FORWARD(F, f), - proj = HPX_FORWARD(Proj, proj)]( - type& t) mutable -> void { - if (HPX_INVOKE(f, HPX_INVOKE(proj, t))) - { - t = new_value; - } - }, - util::projection_identity()); + return sequential_replace_if( + HPX_FORWARD(ExPolicy, policy), first, last, + HPX_FORWARD(F, f), new_value, HPX_FORWARD(Proj, proj)); } }; /// \endcond @@ -673,23 +625,6 @@ namespace hpx { namespace parallel { inline namespace v1 { namespace detail { /// \cond NOINTERNAL - // sequential replace_copy - template - inline util::in_out_result sequential_replace_copy( - InIter first, Sent sent, OutIter dest, T const& old_value, - T const& new_value, Proj&& proj) - { - for (/* */; first != sent; ++first) - { - if (HPX_INVOKE(proj, *first) == old_value) - *dest++ = new_value; - else - *dest++ = *first; - } - return util::in_out_result(first, dest); - } - template struct replace_copy : public detail::algorithm, IterPair> @@ -701,11 +636,12 @@ namespace hpx { namespace parallel { inline namespace v1 { template - static util::in_out_result sequential(ExPolicy, - InIter first, Sent sent, OutIter dest, T const& old_value, - T const& new_value, Proj&& proj) + static util::in_out_result sequential( + ExPolicy&& policy, InIter first, Sent sent, OutIter dest, + T const& old_value, T const& new_value, Proj&& proj) { - return sequential_replace_copy(first, sent, dest, old_value, + return sequential_replace_copy( + HPX_FORWARD(ExPolicy, policy), first, sent, dest, old_value, new_value, HPX_FORWARD(Proj, proj)); } @@ -717,24 +653,9 @@ namespace hpx { namespace parallel { inline namespace v1 { FwdIter2 dest, T const& old_value, T const& new_value, Proj&& proj) { - typedef hpx::util::zip_iterator - zip_iterator; - typedef typename zip_iterator::reference reference; - - return util::detail::get_in_out_result( - for_each_n().call( - HPX_FORWARD(ExPolicy, policy), - hpx::util::make_zip_iterator(first, dest), - detail::distance(first, sent), - [old_value, new_value, proj = HPX_FORWARD(Proj, proj)]( - reference t) -> void { - using hpx::get; - if (HPX_INVOKE(proj, get<0>(t)) == old_value) - get<1>(t) = new_value; - else - get<1>(t) = get<0>(t); //-V573 - }, - util::projection_identity())); + return sequential_replace_copy( + HPX_FORWARD(ExPolicy, policy), first, sent, dest, old_value, + new_value, HPX_FORWARD(Proj, proj)); } }; /// \endcond @@ -777,27 +698,6 @@ namespace hpx { namespace parallel { inline namespace v1 { namespace detail { /// \cond NOINTERNAL - // sequential replace_copy_if - template - inline util::in_out_result sequential_replace_copy_if( - InIter first, Sent sent, OutIter dest, F&& f, T const& new_value, - Proj&& proj) - { - for (/* */; first != sent; ++first) - { - if (HPX_INVOKE(f, HPX_INVOKE(proj, *first))) - { - *dest++ = new_value; - } - else - { - *dest++ = *first; - } - } - return util::in_out_result{first, dest}; - } - template struct replace_copy_if : public detail::algorithm, IterPair> @@ -809,11 +709,12 @@ namespace hpx { namespace parallel { inline namespace v1 { template - static util::in_out_result sequential(ExPolicy, - InIter first, Sent sent, OutIter dest, F&& f, + static util::in_out_result sequential( + ExPolicy&& policy, InIter first, Sent sent, OutIter dest, F&& f, T const& new_value, Proj&& proj) { - return sequential_replace_copy_if(first, sent, dest, + return sequential_replace_copy_if( + HPX_FORWARD(ExPolicy, policy), first, sent, dest, HPX_FORWARD(F, f), new_value, HPX_FORWARD(Proj, proj)); } @@ -824,29 +725,9 @@ namespace hpx { namespace parallel { inline namespace v1 { parallel(ExPolicy&& policy, FwdIter1 first, Sent sent, FwdIter2 dest, F&& f, T const& new_value, Proj&& proj) { - typedef hpx::util::zip_iterator - zip_iterator; - typedef typename zip_iterator::reference reference; - - return util::detail::get_in_out_result( - for_each_n().call( - HPX_FORWARD(ExPolicy, policy), - hpx::util::make_zip_iterator(first, dest), - detail::distance(first, sent), - [new_value, f = HPX_FORWARD(F, f), - proj = HPX_FORWARD(Proj, proj)]( - reference t) mutable -> void { - using hpx::get; - if (HPX_INVOKE(f, HPX_INVOKE(proj, get<0>(t)))) - { - get<1>(t) = new_value; - } - else - { - get<1>(t) = get<0>(t); //-V573 - } - }, - util::projection_identity())); + return sequential_replace_copy_if( + HPX_FORWARD(ExPolicy, policy), first, sent, dest, + HPX_FORWARD(F, f), new_value, HPX_FORWARD(Proj, proj)); } }; /// \endcond @@ -984,11 +865,9 @@ namespace hpx { static_assert((hpx::traits::is_forward_iterator::value), "Required at least forward iterator."); - typedef typename std::iterator_traits::value_type Type; - return hpx::replace_if( HPX_FORWARD(ExPolicy, policy), first, last, - [old_value](Type const& a) -> bool { return old_value == a; }, + [old_value](auto const& a) { return old_value == a; }, new_value); } } replace{}; @@ -1114,11 +993,9 @@ namespace hpx { static_assert((hpx::traits::is_forward_iterator::value), "Required at least forward iterator."); - typedef typename std::iterator_traits::value_type Type; - return hpx::replace_copy_if( HPX_FORWARD(ExPolicy, policy), first, last, dest, - [old_value](Type const& a) -> bool { return old_value == a; }, + [old_value](auto const& a) { return old_value == a; }, new_value); } } replace_copy{}; diff --git a/libs/core/algorithms/include/hpx/parallel/datapar.hpp b/libs/core/algorithms/include/hpx/parallel/datapar.hpp index 1d2b67c916bd..f4973727628b 100644 --- a/libs/core/algorithms/include/hpx/parallel/datapar.hpp +++ b/libs/core/algorithms/include/hpx/parallel/datapar.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/libs/core/algorithms/include/hpx/parallel/datapar/replace.hpp b/libs/core/algorithms/include/hpx/parallel/datapar/replace.hpp new file mode 100644 index 000000000000..888fa1e5f4f3 --- /dev/null +++ b/libs/core/algorithms/include/hpx/parallel/datapar/replace.hpp @@ -0,0 +1,306 @@ +// Copyright (c) 2022 Srinivas Yadav +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include + +#if defined(HPX_HAVE_DATAPAR) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace hpx { namespace parallel { inline namespace v1 { namespace detail { + + /////////////////////////////////////////////////////////////////////////// + template + struct datapar_replace + { + template + HPX_HOST_DEVICE HPX_FORCEINLINE static auto call(ExPolicy&& policy, + InIter first, InIter last, T1 const& old_value, T2 const& new_value, + Proj&& proj) + { + if constexpr (hpx::is_sequenced_execution_policy_v) + { + return util::loop_ind(HPX_FORWARD(ExPolicy, policy), first, + last, [old_value, new_value, &proj](auto& v) { + using var_type = std::decay_t; + traits::mask_assign( + HPX_INVOKE(proj, v) == var_type(old_value), v, + var_type(new_value)); + }); + } + else + { + return for_each_n().call( + HPX_FORWARD(ExPolicy, policy), first, + std::distance(first, last), + [old_value, new_value, proj = HPX_FORWARD(Proj, proj)]( + auto& v) -> void { + traits::mask_assign( + HPX_INVOKE(proj, v) == var_type(old_value), v, + var_type(new_value)); + }, + util::projection_identity()); + } + } + }; + + template ::value)> + HPX_HOST_DEVICE HPX_FORCEINLINE auto tag_invoke( + sequential_replace_t, ExPolicy&& policy, InIter first, + InIter last, T1 const& old_value, T2 const& new_value, Proj&& proj) + { + if constexpr (hpx::parallel::util::detail::iterator_datapar_compatible< + InIter>::value) + { + return datapar_replace::call( + HPX_FORWARD(ExPolicy, policy), first, last, old_value, + new_value, HPX_FORWARD(Proj, proj)); + } + else + { + using base_policy_type = + decltype((hpx::execution::experimental::to_non_simd( + std::declval()))); + return sequential_replace( + hpx::execution::experimental::to_non_simd(policy), first, last, + old_value, new_value, HPX_FORWARD(Proj, proj)); + } + } + + /////////////////////////////////////////////////////////////////////////// + template + struct datapar_replace_if + { + template + HPX_HOST_DEVICE HPX_FORCEINLINE static auto call(ExPolicy&& policy, + InIter first, Sent last, F&& f, T const& new_value, Proj&& proj) + { + if constexpr (hpx::is_sequenced_execution_policy_v) + { + return util::loop_ind(HPX_FORWARD(ExPolicy, policy), first, + last, [&f, new_value, &proj](auto& v) { + using var_type = std::decay_t; + traits::mask_assign(HPX_INVOKE(f, HPX_INVOKE(proj, v)), + v, var_type(new_value)); + }); + } + else + { + return for_each_n().call( + HPX_FORWARD(ExPolicy, policy), first, + detail::distance(first, last), + [new_value, f = HPX_FORWARD(F, f), + proj = HPX_FORWARD(Proj, proj)]( + auto& v) mutable -> void { + using var_type = std::decay_t; + traits::mask_assign( + HPX_INVOKE(f, (HPX_INVOKE(proj, v))), v, + var_type(new_value)); + }, + util::projection_identity()); + } + } + }; + + template ::value)> + HPX_HOST_DEVICE HPX_FORCEINLINE auto tag_invoke( + sequential_replace_if_t, ExPolicy&& policy, InIter first, + Sent last, F&& f, T const& new_value, Proj&& proj) + { + if constexpr (hpx::parallel::util::detail::iterator_datapar_compatible< + InIter>::value) + { + return datapar_replace_if::call( + HPX_FORWARD(ExPolicy, policy), first, last, HPX_FORWARD(F, f), + new_value, HPX_FORWARD(Proj, proj)); + } + else + { + using base_policy_type = + decltype((hpx::execution::experimental::to_non_simd( + std::declval()))); + return sequential_replace_if( + hpx::execution::experimental::to_non_simd(policy), first, last, + HPX_FORWARD(F, f), new_value, HPX_FORWARD(Proj, proj)); + } + } + + /////////////////////////////////////////////////////////////////////////// + template + struct datapar_replace_copy + { + template + HPX_HOST_DEVICE HPX_FORCEINLINE static auto call(ExPolicy&& policy, + InIter first, Sent sent, OutIter dest, T const& old_value, + T const& new_value, Proj&& proj) + { + if constexpr (hpx::is_sequenced_execution_policy_v) + { + std::size_t n = detail::distance(first, sent); + + util::loop_n_ind( + hpx::util::make_zip_iterator(first, dest), sent, + [old_value, new_value, proj = HPX_FORWARD(Proj, proj)]( + auto& v) { + using var_type = std::decay_t(v))>; + get<1>(v) = traits::choose( + HPX_INVOKE(proj, get<0>(v)) == var_type(old_value), + var_type(new_value), get<0>(v)); + }); + + return util::in_out_result( + first + n, dest + n); + } + else + { + typedef hpx::util::zip_iterator zip_iterator; + + return util::detail::get_in_out_result( + for_each_n().call( + HPX_FORWARD(ExPolicy, policy), + hpx::util::make_zip_iterator(first, dest), + detail::distance(first, sent), + [old_value, new_value, proj = HPX_FORWARD(Proj, proj)]( + auto& v) -> void { + using hpx::get; + using var_type = std::decay_t(v))>; + get<1>(v) = + traits::choose(HPX_INVOKE(proj, get<0>(v)) == + var_type(old_value), + var_type(new_value), get<0>(v)); + }, + util::projection_identity())); + } + } + }; + + template ::value)> + HPX_HOST_DEVICE HPX_FORCEINLINE auto tag_invoke( + sequential_replace_copy_t, ExPolicy&& policy, InIter first, + Sent sent, OutIter dest, T const& old_value, T const& new_value, + Proj&& proj) + { + if constexpr (hpx::parallel::util::detail::iterator_datapar_compatible< + InIter>::value) + { + return datapar_replace_copy::call( + (HPX_FORWARD(ExPolicy, policy), first, sent, dest, old_value, + new_value, HPX_FORWARD(Proj, proj))); + } + else + { + using base_policy_type = + decltype((hpx::execution::experimental::to_non_simd( + std::declval()))); + return sequential_replace_copy( + hpx::execution::experimental::to_non_simd(policy), first, sent, + dest, old_value, new_value, HPX_FORWARD(Proj, proj)); + } + } + + /////////////////////////////////////////////////////////////////////////// + template + struct datapar_replace_copy_if + { + template + HPX_HOST_DEVICE HPX_FORCEINLINE static auto call(ExPolicy&& policy, + InIter first, Sent last, OutIter dest, F&& f, T const& new_value, + Proj&& proj) + { + if constexpr (hpx::is_sequenced_execution_policy_v) + { + std::size_t n = detail::distance(first, last); + + util::loop_n_ind( + hpx::util::make_zip_iterator(first, dest), n, + [new_value, f = HPX_FORWARD(F, f), + proj = HPX_FORWARD(Proj, proj)]( + auto& v) mutable -> void { + using hpx::get; + using var_type = std::decay_t(v))>; + get<1>(v) = traits::choose( + HPX_INVOKE(f, HPX_INVOKE(proj, get<0>(v))), + var_type(new_value), get<0>(v)); + }); + return util::in_out_result( + first + n, dest + n); + } + else + { + typedef hpx::util::zip_iterator zip_iterator; + + return util::detail::get_in_out_result( + for_each_n().call( + HPX_FORWARD(ExPolicy, policy), + hpx::util::make_zip_iterator(first, dest), + detail::distance(first, last), + [new_value, f = HPX_FORWARD(F, f), + proj = HPX_FORWARD(Proj, proj)]( + auto& v) mutable -> void { + using hpx::get; + using var_type = std::decay_t(v))>; + get<1>(v) = traits::choose( + HPX_INVOKE(f, HPX_INVOKE(proj, get<0>(v))), + var_type(new_value), get<0>(v)); + }, + util::projection_identity())); + } + } + }; + + template ::value)> + HPX_HOST_DEVICE HPX_FORCEINLINE auto tag_invoke( + sequential_replace_copy_if_t, ExPolicy&& policy, InIter first, + Sent last, OutIter dest, F&& f, T const& new_value, Proj&& proj) + { + if constexpr (hpx::parallel::util::detail::iterator_datapar_compatible< + InIter>::value) + { + return datapar_replace_copy_if::call( + HPX_FORWARD(ExPolicy, policy), first, last, dest, + HPX_FORWARD(F, f), new_value, HPX_FORWARD(Proj, proj)); + } + else + { + using base_policy_type = + decltype((hpx::execution::experimental::to_non_simd( + std::declval()))); + return sequential_replace_copy_if( + hpx::execution::experimental::to_non_simd(policy), first, last, + dest, HPX_FORWARD(F, f), new_value, HPX_FORWARD(Proj, proj)); + } + } +}}}} // namespace hpx::parallel::v1::detail +#endif diff --git a/libs/core/algorithms/tests/unit/datapar_algorithms/CMakeLists.txt b/libs/core/algorithms/tests/unit/datapar_algorithms/CMakeLists.txt index 9cc59a8fa4bb..cd15949a449c 100644 --- a/libs/core/algorithms/tests/unit/datapar_algorithms/CMakeLists.txt +++ b/libs/core/algorithms/tests/unit/datapar_algorithms/CMakeLists.txt @@ -35,6 +35,10 @@ if(HPX_WITH_DATAPAR) mismatch_datapar none_of_datapar reduce_datapar + replace_copy_if_datapar + replace_copy_datapar + replace_datapar + replace_if_datapar transform_binary_datapar transform_binary2_datapar transform_datapar diff --git a/libs/core/algorithms/tests/unit/datapar_algorithms/replace_copy_datapar.cpp b/libs/core/algorithms/tests/unit/datapar_algorithms/replace_copy_datapar.cpp new file mode 100644 index 000000000000..03fdd259cc6f --- /dev/null +++ b/libs/core/algorithms/tests/unit/datapar_algorithms/replace_copy_datapar.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2022 Srinivas Yadav +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../algorithms/test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////// +template +void test_replace_copy(ExPolicy policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::vector d1(c.size()); + std::vector d2(c.size()); //-V656 + + std::iota(std::begin(c), std::end(c), std::rand()); + + std::size_t idx = std::rand() % c.size(); //-V104 + + hpx::replace_copy(policy, iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d1), c[idx], c[idx] + 1); + + std::replace_copy( + std::begin(c), std::end(c), std::begin(d2), c[idx], c[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(d1), std::end(d1), std::begin(d2), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d1.size()); +} + +template +void test_replace_copy_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::vector d1(c.size()); + std::vector d2(c.size()); //-V656 + + std::iota(std::begin(c), std::end(c), std::rand()); + + std::size_t idx = std::rand() % c.size(); //-V104 + + auto f = hpx::replace_copy(p, iterator(std::begin(c)), + iterator(std::end(c)), std::begin(d1), c[idx], c[idx] + 1); + f.wait(); + + std::replace_copy( + std::begin(c), std::end(c), std::begin(d2), c[idx], c[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(d1), std::end(d1), std::begin(d2), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d1.size()); +} + +template +void test_replace_copy() +{ + using namespace hpx::execution; + test_replace_copy(simd, IteratorTag()); + test_replace_copy(par_simd, IteratorTag()); + + test_replace_copy_async(simd(task), IteratorTag()); + test_replace_copy_async(par_simd(task), IteratorTag()); +} + +void replace_copy_test() +{ + test_replace_copy(); + test_replace_copy(); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + replace_copy_test(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/datapar_algorithms/replace_copy_if_datapar.cpp b/libs/core/algorithms/tests/unit/datapar_algorithms/replace_copy_if_datapar.cpp new file mode 100644 index 000000000000..5410e0c8b011 --- /dev/null +++ b/libs/core/algorithms/tests/unit/datapar_algorithms/replace_copy_if_datapar.cpp @@ -0,0 +1,153 @@ +// Copyright (c) 2022 Srinivas Yadav +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../algorithms/test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////// +struct equal_f +{ + equal_f(int val) + : val_(val) + { + } + + template + auto operator()(T lhs) const + { + return lhs == val_; + } + + int val_; +}; + +template +void test_replace_copy_if(ExPolicy policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::vector d1(c.size()); + std::vector d2(c.size()); //-V656 + + std::iota(std::begin(c), std::end(c), std::rand()); + + std::size_t idx = std::rand() % c.size(); //-V104 + + hpx::replace_copy_if(policy, iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d1), equal_f(c[idx]), c[idx] + 1); + + std::replace_copy_if(std::begin(c), std::end(c), std::begin(d2), + equal_f(c[idx]), c[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(d1), std::end(d1), std::begin(d2), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d1.size()); +} + +template +void test_replace_copy_if_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::vector d1(c.size()); + std::vector d2(c.size()); //-V656 + + std::iota(std::begin(c), std::end(c), std::rand()); + + std::size_t idx = std::rand() % c.size(); //-V104 + + auto f = hpx::replace_copy_if(p, iterator(std::begin(c)), + iterator(std::end(c)), std::begin(d1), equal_f(c[idx]), c[idx] + 1); + f.wait(); + + std::replace_copy_if(std::begin(c), std::end(c), std::begin(d2), + equal_f(c[idx]), c[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(d1), std::end(d1), std::begin(d2), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d1.size()); +} + +template +void test_replace_copy_if() +{ + using namespace hpx::execution; + test_replace_copy_if(simd, IteratorTag()); + test_replace_copy_if(par_simd, IteratorTag()); + + test_replace_copy_if_async(simd(task), IteratorTag()); + test_replace_copy_if_async(par_simd(task), IteratorTag()); +} + +void replace_copy_if_test() +{ + test_replace_copy_if(); + test_replace_copy_if(); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + replace_copy_if_test(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/datapar_algorithms/replace_datapar.cpp b/libs/core/algorithms/tests/unit/datapar_algorithms/replace_datapar.cpp new file mode 100644 index 000000000000..4c462be731d2 --- /dev/null +++ b/libs/core/algorithms/tests/unit/datapar_algorithms/replace_datapar.cpp @@ -0,0 +1,134 @@ +// Copyright (c) 2022 Srinivas Yadav +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../algorithms/test_utils.hpp" + +/////////////////////////////////////////////////////////////////////////////// +template +void test_replace(ExPolicy policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + std::copy(std::begin(c), std::end(c), std::begin(d)); + + std::size_t idx = std::rand() % c.size(); //-V104 + + hpx::replace(policy, iterator(std::begin(c)), iterator(std::end(c)), c[idx], + c[idx] + 1); + + std::replace(std::begin(d), std::end(d), d[idx], d[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(c), std::end(c), std::begin(d), + [&count](auto v1, auto v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d.size()); +} + +template +void test_replace_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + std::copy(std::begin(c), std::end(c), std::begin(d)); + + std::size_t idx = std::rand() % c.size(); //-V104 + + hpx::future f = hpx::replace( + p, iterator(std::begin(c)), iterator(std::end(c)), c[idx], c[idx] + 1); + f.wait(); + + std::replace(std::begin(d), std::end(d), d[idx], d[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(c), std::end(c), std::begin(d), + [&count](auto v1, auto v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d.size()); +} + +template +void test_replace() +{ + using namespace hpx::execution; + test_replace(simd, IteratorTag()); + test_replace(par_simd, IteratorTag()); + + test_replace_async(simd(task), IteratorTag()); + test_replace_async(par_simd(task), IteratorTag()); +} + +void replace_test() +{ + test_replace(); + test_replace(); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + replace_test(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/datapar_algorithms/replace_if_datapar.cpp b/libs/core/algorithms/tests/unit/datapar_algorithms/replace_if_datapar.cpp new file mode 100644 index 000000000000..d93e57178a0f --- /dev/null +++ b/libs/core/algorithms/tests/unit/datapar_algorithms/replace_if_datapar.cpp @@ -0,0 +1,150 @@ +// Copyright (c) 2022 Srinivas Yadav +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../algorithms/test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////// +struct equal_f +{ + equal_f(int val) + : val_(val) + { + } + + template + auto operator()(T lhs) const + { + return lhs == T(val_); + } + + int val_; +}; + +template +void test_replace_if(ExPolicy policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + std::copy(std::begin(c), std::end(c), std::begin(d)); + + std::size_t idx = std::rand() % c.size(); //-V104 + + hpx::replace_if(policy, iterator(std::begin(c)), iterator(std::end(c)), + equal_f(c[idx]), c[idx] + 1); + + std::replace_if(std::begin(d), std::end(d), equal_f(d[idx]), d[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(c), std::end(c), std::begin(d), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d.size()); +} + +template +void test_replace_if_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + std::copy(std::begin(c), std::end(c), std::begin(d)); + + std::size_t idx = std::rand() % c.size(); //-V104 + + hpx::future f = hpx::replace_if(p, iterator(std::begin(c)), + iterator(std::end(c)), equal_f(c[idx]), c[idx] + 1); + f.wait(); + + std::replace_if(std::begin(d), std::end(d), equal_f(d[idx]), d[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(c), std::end(c), std::begin(d), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d.size()); +} + +template +void test_replace_if() +{ + using namespace hpx::execution; + test_replace_if(simd, IteratorTag()); + test_replace_if(par_simd, IteratorTag()); + + test_replace_if_async(simd(task), IteratorTag()); + test_replace_if_async(par_simd(task), IteratorTag()); +} + +void replace_if_test() +{ + test_replace_if(); + test_replace_if(); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + replace_if_test(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/asio/include/hpx/asio/asio_util.hpp b/libs/core/asio/include/hpx/asio/asio_util.hpp index 9de4b56b339c..23b372f50745 100644 --- a/libs/core/asio/include/hpx/asio/asio_util.hpp +++ b/libs/core/asio/include/hpx/asio/asio_util.hpp @@ -27,7 +27,7 @@ namespace hpx { namespace util { /////////////////////////////////////////////////////////////////////////// HPX_CORE_EXPORT bool get_endpoint(std::string const& addr, - std::uint16_t port, asio::ip::tcp::endpoint& ep); + std::uint16_t port, asio::ip::tcp::endpoint& ep, bool force_ipv4 = false); HPX_CORE_EXPORT std::string get_endpoint_name( asio::ip::tcp::endpoint const& ep); @@ -36,7 +36,7 @@ namespace hpx { namespace util { // properly resolve a give host name to the corresponding IP address HPX_CORE_EXPORT asio::ip::tcp::endpoint resolve_hostname( std::string const& hostname, std::uint16_t port, - asio::io_context& io_service); + asio::io_context& io_service, bool force_ipv4 = false); /////////////////////////////////////////////////////////////////////////// // return the public IP address of the local node diff --git a/libs/core/asio/include/hpx/asio/map_hostnames.hpp b/libs/core/asio/include/hpx/asio/map_hostnames.hpp index 73db45f4129f..e5f66a786d02 100644 --- a/libs/core/asio/include/hpx/asio/map_hostnames.hpp +++ b/libs/core/asio/include/hpx/asio/map_hostnames.hpp @@ -27,7 +27,7 @@ namespace hpx { namespace util { transform_function_type; map_hostnames(bool debug = false) - : debug_(debug) + : ipv4_(false), debug_(debug) { } @@ -46,12 +46,18 @@ namespace hpx { namespace util { transform_ = f; } + void force_ipv4(bool f) + { + ipv4_ = f; + } + std::string map(std::string host_name, std::uint16_t port) const; private: transform_function_type transform_; std::string suffix_; std::string prefix_; + bool ipv4_; bool debug_; }; }} // namespace hpx::util diff --git a/libs/core/asio/src/asio_util.cpp b/libs/core/asio/src/asio_util.cpp index 53086784f39c..89af33ca1ece 100644 --- a/libs/core/asio/src/asio_util.cpp +++ b/libs/core/asio/src/asio_util.cpp @@ -52,7 +52,7 @@ namespace hpx { namespace util { /////////////////////////////////////////////////////////////////////////// bool get_endpoint(std::string const& addr, std::uint16_t port, - asio::ip::tcp::endpoint& ep) + asio::ip::tcp::endpoint& ep, bool force_ipv4) { using namespace asio::ip; std::error_code ec; @@ -63,11 +63,14 @@ namespace hpx { namespace util { return true; } - address_v6 addr6 = address_v6::from_string(addr.c_str(), ec); - if (!ec) - { // it's an IPV6 address - ep = tcp::endpoint(address(addr6), port); - return true; + if (!force_ipv4) + { + address_v6 addr6 = address_v6::from_string(addr.c_str(), ec); + if (!ec) + { // it's an IPV6 address + ep = tcp::endpoint(address(addr6), port); + return true; + } } return false; } @@ -81,7 +84,7 @@ namespace hpx { namespace util { /////////////////////////////////////////////////////////////////////////// // properly resolve a give host name to the corresponding IP address asio::ip::tcp::endpoint resolve_hostname(std::string const& hostname, - std::uint16_t port, asio::io_context& io_service) + std::uint16_t port, asio::io_context& io_service, bool force_ipv4) { using asio::ip::tcp; @@ -108,6 +111,13 @@ namespace hpx { namespace util { tcp::resolver::query query(hostname, std::to_string(port)); asio::ip::tcp::resolver::iterator it = resolver.resolve(query); + + while (force_ipv4 && + it != tcp::resolver::iterator() && !it->endpoint().address().is_v4()) + { + ++it; + } + HPX_ASSERT(it != asio::ip::tcp::resolver::iterator()); return *it; } diff --git a/libs/core/asio/src/map_hostnames.cpp b/libs/core/asio/src/map_hostnames.cpp index 8c6d6aa4a861..56d3249625b8 100644 --- a/libs/core/asio/src/map_hostnames.cpp +++ b/libs/core/asio/src/map_hostnames.cpp @@ -48,7 +48,7 @@ namespace hpx { namespace util { // do full host name resolution asio::io_context io_service; asio::ip::tcp::endpoint ep = util::resolve_hostname( - prefix_ + host_name + suffix_, port, io_service); + prefix_ + host_name + suffix_, port, io_service, ipv4_); std::string resolved_addr(util::get_endpoint_name(ep)); if (debug_) diff --git a/libs/core/async_base/include/hpx/async_base/async.hpp b/libs/core/async_base/include/hpx/async_base/async.hpp index 60cf689b6627..ad1dd88e4c0b 100644 --- a/libs/core/async_base/include/hpx/async_base/async.hpp +++ b/libs/core/async_base/include/hpx/async_base/async.hpp @@ -28,8 +28,8 @@ namespace hpx { /// with arguments \a ts according to a specific launch policy. /// - If the async flag is set (i.e. (policy & hpx::launch::async) != 0), then /// async executes the callable object f on a new thread of execution (with all - /// thread-locals initialized) as if spawned by hpx::thread(hpx::forward(f), - /// hpx::forward(ts)...), except that if the function f returns a value + /// thread-locals initialized) as if spawned by hpx::thread(std::forward(f), + /// std::forward(ts)...), except that if the function f returns a value /// or throws an exception, it is stored in the shared state accessible through /// the hpx::future that async returns to the caller. /// - If the deferred flag is set (i.e. (policy & hpx::launch::deferred) != 0), diff --git a/libs/core/command_line_handling_local/src/parse_command_line_local.cpp b/libs/core/command_line_handling_local/src/parse_command_line_local.cpp index 51c3b537dd0c..ac53ff851f80 100644 --- a/libs/core/command_line_handling_local/src/parse_command_line_local.cpp +++ b/libs/core/command_line_handling_local/src/parse_command_line_local.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2014 Hartmut Kaiser +// Copyright (c) 2007-2022 Hartmut Kaiser // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -280,6 +280,19 @@ namespace hpx { namespace local { namespace detail { } } + void verify_unknown_options(std::vector const& opts) + { + for (auto const& opt : opts) + { + std::string::size_type p = opt.find("--hpx:"); + if (p != std::string::npos) + { + throw hpx::detail::command_line_error( + "Unknown/misspelled HPX command line option found: " + opt); + } + } + } + /////////////////////////////////////////////////////////////////////////// // parse the command line bool parse_commandline(util::section const& rtcfg, @@ -468,6 +481,8 @@ namespace hpx { namespace local { namespace detail { using hpx::program_options::exclude_positional; *unregistered_options = collect_unrecognized(opts.options, exclude_positional); + + verify_unknown_options(*unregistered_options); } store(opts, vm); @@ -491,6 +506,8 @@ namespace hpx { namespace local { namespace detail { using hpx::program_options::include_positional; *unregistered_options = collect_unrecognized(opts.options, include_positional); + + verify_unknown_options(*unregistered_options); } store(opts, vm); diff --git a/libs/core/execution/include/hpx/execution/algorithms/sync_wait.hpp b/libs/core/execution/include/hpx/execution/algorithms/sync_wait.hpp index 06a75059682b..4f1136089ef5 100644 --- a/libs/core/execution/include/hpx/execution/algorithms/sync_wait.hpp +++ b/libs/core/execution/include/hpx/execution/algorithms/sync_wait.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2020 ETH Zurich // Copyright (c) 2022 Hartmut Kaiser +// Copyright (c) 2022 Chuanqiu He // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -37,6 +38,12 @@ namespace hpx::execution::experimental::detail { + enum class sync_wait_type + { + single, + variant + }; + struct sync_wait_error_visitor { void operator()(std::exception_ptr ep) const @@ -84,7 +91,27 @@ namespace hpx::execution::experimental::detail { template using make_decayed_pack_t = typename make_decayed_pack::type; - template + /////////////////////////////////////////////////////////////////////////// + template + struct select_result; + + template + struct select_result + { + using type = hpx::variant>>; + }; + + template + struct select_result + { + using type = T; + }; + + template + using select_result_t = typename select_result::type; + + /////////////////////////////////////////////////////////////////////////// + template struct sync_wait_receiver { // value and error_types of the predecessor sender @@ -104,13 +131,12 @@ namespace hpx::execution::experimental::detail { // value_types for a sender. In particular, split() explicitly adds a // const& to all tuple members in a way that prevent simply passing // decayed_tuple to predecessor_value_types. - using single_result_type = make_decayed_pack_t< - single_variant_t>>; // The template should compute the result type of whatever returned from - // sync_wait, which should be optional of the variant of the tuples. The - // sync_wait works when the variant has one tuple. - using result_type = hpx::variant; + // sync_wait or sync_wait_with_variant by checking sync_wait_type is + // single or variant + using result_type = select_result_t>; // The type of errors to store in the variant. This in itself is a // variant. @@ -131,8 +157,20 @@ namespace hpx::execution::experimental::detail { { // pull the tuple out of the variant and wrap it into an // optional, make sure to remove the references - return hpx::optional( - hpx::get<0>(hpx::get(HPX_MOVE(value)))); + if constexpr (Type == sync_wait_type::single) + { + using single_result_type = make_decayed_pack_t< + single_variant_t>>; + + return hpx::optional(hpx::get<0>( + hpx::get(HPX_MOVE(value)))); + } + else + { + return hpx::optional( + hpx::get(HPX_MOVE(value))); + } } else if (hpx::holds_alternative(value)) { @@ -145,7 +183,17 @@ namespace hpx::execution::experimental::detail { // this means that none of set_value/set_error/set_stopped was // called. HPX_ASSERT(hpx::holds_alternative(value)); - return hpx::optional(); + if constexpr (Type == sync_wait_type::single) + { + using single_result_type = + make_decayed_pack_t>>; + return hpx::optional(); + } + else + { + return hpx::optional(); + } } }; @@ -341,9 +389,10 @@ namespace hpx::this_thread::experimental { hpx::execution::experimental::run_loop_scheduler const& sched, Sender&& sender) { + using hpx::execution::experimental::detail::sync_wait_type; using receiver_type = - hpx::execution::experimental::detail::sync_wait_receiver< - Sender>; + hpx::execution::experimental::detail::sync_wait_receiver; using state_type = typename receiver_type::shared_state; hpx::execution::experimental::run_loop& loop = sched.get_run_loop(); @@ -368,9 +417,10 @@ namespace hpx::this_thread::experimental { friend HPX_FORCEINLINE auto tag_fallback_invoke( sync_wait_t, Sender&& sender) { + using hpx::execution::experimental::detail::sync_wait_type; using receiver_type = - hpx::execution::experimental::detail::sync_wait_receiver< - Sender>; + hpx::execution::experimental::detail::sync_wait_receiver; using state_type = typename receiver_type::shared_state; hpx::execution::experimental::run_loop loop{}; @@ -404,4 +454,116 @@ namespace hpx::this_thread::experimental { sync_wait_t>{}; } } sync_wait{}; + + //////////////////////////////////////////////////////////////////// + // DPO for sync_wait_with_variant + + // this_thread::sync_wait_with_variant is a sender consumer that submits + // the work described by the provided sender for execution, similarly to + // ensure_started, except that it blocks the current std::thread or + // thread of main until the work is completed, and returns an optional + // of variant of tuples that were sent by the provided sender on its + // completion of work. + inline constexpr struct sync_wait_with_variant_t final + : hpx::functional::detail::tag_priority + { + private: + // clang-format off + template && + hpx::execution::experimental::detail:: + is_completion_scheduler_tag_invocable_v< + hpx::execution::experimental::set_value_t, + Sender, sync_wait_with_variant_t + > + )> + // clang-format on + friend constexpr HPX_FORCEINLINE auto tag_override_invoke( + sync_wait_with_variant_t, Sender&& sender) + { + auto scheduler = + hpx::execution::experimental::get_completion_scheduler< + hpx::execution::experimental::set_value_t>(sender); + + return hpx::functional::tag_invoke(sync_wait_with_variant_t{}, + HPX_MOVE(scheduler), HPX_FORWARD(Sender, sender)); + } + + // clang-format off + template + )> + // clang-format on + friend auto tag_invoke(sync_wait_with_variant_t, + hpx::execution::experimental::run_loop_scheduler const& sched, + Sender&& sender) + { + using hpx::execution::experimental::detail::sync_wait_type; + using receiver_type = + hpx::execution::experimental::detail::sync_wait_receiver; + using state_type = typename receiver_type::shared_state; + + hpx::execution::experimental::run_loop& loop = sched.get_run_loop(); + state_type state{}; + auto op_state = hpx::execution::experimental::connect( + HPX_FORWARD(Sender, sender), receiver_type{state, loop}); + hpx::execution::experimental::start(op_state); + + // Wait for the variant to be filled in. + loop.run(); + + return state.get_value(); + } + + // clang-format off + template + )> + // clang-format on + friend HPX_FORCEINLINE auto tag_fallback_invoke( + sync_wait_with_variant_t, Sender&& sender) + { + using hpx::execution::experimental::detail::sync_wait_type; + using receiver_type = + hpx::execution::experimental::detail::sync_wait_receiver; + using state_type = typename receiver_type::shared_state; + + hpx::execution::experimental::run_loop loop{}; + state_type state{}; + auto op_state = hpx::execution::experimental::connect( + HPX_FORWARD(Sender, sender), receiver_type{state, loop}); + hpx::execution::experimental::start(op_state); + + // Wait for the variant to be filled in. + loop.run(); + + return state.get_value(); + } + + // clang-format off + template + )> + // clang-format on + friend constexpr HPX_FORCEINLINE auto tag_fallback_invoke( + sync_wait_with_variant_t, Scheduler&& scheduler) + { + return hpx::execution::experimental::detail::inject_scheduler< + sync_wait_with_variant_t, Scheduler>{ + HPX_FORWARD(Scheduler, scheduler)}; + } + + friend constexpr HPX_FORCEINLINE auto tag_fallback_invoke( + sync_wait_with_variant_t) + { + return hpx::execution::experimental::detail::partial_algorithm< + sync_wait_with_variant_t>{}; + } + } sync_wait_with_variant{}; } // namespace hpx::this_thread::experimental diff --git a/libs/core/execution/tests/unit/CMakeLists.txt b/libs/core/execution/tests/unit/CMakeLists.txt index 87a295db2e49..3152cf28af38 100644 --- a/libs/core/execution/tests/unit/CMakeLists.txt +++ b/libs/core/execution/tests/unit/CMakeLists.txt @@ -18,6 +18,7 @@ set(tests algorithm_split algorithm_start_detached algorithm_sync_wait + algorithm_sync_wait_with_variant algorithm_then algorithm_transfer algorithm_transfer_just diff --git a/libs/core/execution/tests/unit/algorithm_sync_wait_with_variant.cpp b/libs/core/execution/tests/unit/algorithm_sync_wait_with_variant.cpp new file mode 100644 index 000000000000..232a64217549 --- /dev/null +++ b/libs/core/execution/tests/unit/algorithm_sync_wait_with_variant.cpp @@ -0,0 +1,295 @@ +// Copyright (c) 2022 Hartmut Kaiser +// Copyright (c) 2022 Chuanqiu He +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include "algorithm_test_utils.hpp" + +#include +#include +#include +#include +#include +#include + +namespace ex = hpx::execution::experimental; +namespace tt = hpx::this_thread::experimental; + +// NOTE: This is not a conforming sync_wait_with_variant implementation. +// It only exists to check that the tag_invoke overload is called. +void tag_invoke(tt::sync_wait_with_variant_t, custom_sender2 s) +{ + s.tag_invoke_overload_called = true; +} + +int hpx_main() +{ + // Success path + { + std::atomic start_called{false}; + std::atomic connect_called{false}; + std::atomic tag_invoke_overload_called{false}; + tt::sync_wait_with_variant(custom_sender{ + start_called, connect_called, tag_invoke_overload_called}); + HPX_TEST(start_called); + HPX_TEST(connect_called); + HPX_TEST(!tag_invoke_overload_called); + } + // sync_wait_with_variant can accept single value senders : + // assume currently have one tuple + { + auto result = ex::just(42) | tt::sync_wait_with_variant(); + + auto v = *result; + static_assert( + std::is_same_v>>); + HPX_TEST(hpx::holds_alternative>(v)); + + auto t = hpx::get>(v); + static_assert(std::is_same_v>); + + auto i = hpx::get<0>(t); + static_assert(std::is_same_v); + + HPX_TEST(i == 42); + } + + { + auto result = ex::just(3, 4.0) | tt::sync_wait_with_variant(); + + auto v = *result; + static_assert( + std::is_same_v>>); + + auto t = hpx::get>(v); + static_assert(std::is_same_v>); + + auto i = hpx::get<0>(t); + static_assert(std::is_same_v); + + HPX_TEST(i == 3); + + auto j = hpx::get<1>(t); + static_assert(std::is_same_v); + + HPX_TEST(j == 4.0); + } + + { + auto result = + tt::sync_wait_with_variant(ex::just(3, 4.0, std::string("42"))); + auto v = *result; + + static_assert(std::is_same_v>>); + + auto t = hpx::get>(v); + static_assert( + std::is_same_v>); + + auto i = hpx::get<0>(t); + static_assert(std::is_same_v); + + HPX_TEST(i == 3); + + auto j = hpx::get<1>(t); + static_assert(std::is_same_v); + + HPX_TEST(j == 4.0); + + auto k = hpx::get<2>(t); + static_assert(std::is_same_v); + + HPX_TEST(k == "42"); + } + + { + auto s1 = ex::just(custom_type_non_default_constructible{42}); + auto result = tt::sync_wait_with_variant(s1); + auto v = *result; + check_value_types< + hpx::variant>>( + s1); + + auto t = hpx::get>(v); + auto p = hpx::get<0>(t); + static_assert( + std::is_same_v); + + HPX_TEST_EQ(p.x, 42); + } + + { + auto result = tt::sync_wait_with_variant( + ex::just(custom_type_non_default_constructible_non_copyable{42})); + auto const& v = *result; + static_assert(std::is_same_v, + hpx::variant>>); + + auto const& t = hpx::get< + hpx::tuple>(v); + auto const& p = hpx::get<0>(t); + static_assert(std::is_same_v, + custom_type_non_default_constructible_non_copyable>); + + HPX_TEST_EQ(p.x, 42); + } + + // sync_wait_with_variant can accept more than one senders: + // (accept variant of multi_tuple senders ) + // tests a sender which has two different value types + // Success path + { + std::atomic start_called{false}; + std::atomic connect_called{false}; + std::atomic tag_invoke_overload_called{false}; + tt::sync_wait_with_variant(custom_sender_multi_tuple{ + start_called, connect_called, tag_invoke_overload_called, true}); + HPX_TEST(start_called); + HPX_TEST(connect_called); + HPX_TEST(!tag_invoke_overload_called); + } + + { + std::atomic start_called{false}; + std::atomic connect_called{false}; + std::atomic tag_invoke_overload_called{false}; + tt::sync_wait_with_variant(custom_sender_multi_tuple{ + start_called, connect_called, tag_invoke_overload_called, false}); + HPX_TEST(start_called); + HPX_TEST(connect_called); + HPX_TEST(!tag_invoke_overload_called); + } + + { + auto sd = ex::just(3) | ex::let_error([](std::exception_ptr) { + HPX_TEST(false); + return ex::just(std::string{"err"}); + }); + + auto result = tt::sync_wait_with_variant(std::move(sd)); + + // variant + auto v = *result; + static_assert(std::is_same_v, hpx::tuple>>); + + HPX_TEST(hpx::holds_alternative>(v)); + + // tuple + auto t = hpx::get<1>(v); + static_assert(std::is_same_v>); + + auto i = hpx::get<0>(t); + static_assert(std::is_same_v); + + HPX_TEST_EQ(i, 3); + } + + { + auto s1 = ex::just(custom_type_non_default_constructible{42}); + auto s2 = ex::let_value(std::move(s1), + [](custom_type_non_default_constructible const& value) { + HPX_TEST_EQ(value.x, 42); + return ex::just(std::to_string(value.x)); + }); + + auto result = tt::sync_wait_with_variant(std::move(s2)); + + // variant + auto v = *result; + static_assert( + std::is_same_v>>); + + // tuple + auto t = hpx::get<0>(v); + static_assert(std::is_same_v>); + + auto j = hpx::get<0>(t); + static_assert(std::is_same_v); + + HPX_TEST_EQ(j, std::string("42")); + } + + // operator| overload + { + std::atomic start_called{false}; + std::atomic connect_called{false}; + std::atomic tag_invoke_overload_called{false}; + custom_sender{ + start_called, connect_called, tag_invoke_overload_called} | + tt::sync_wait_with_variant(); + HPX_TEST(start_called); + HPX_TEST(connect_called); + HPX_TEST(!tag_invoke_overload_called); + } + + { + auto result = ex::just(3) | tt::sync_wait_with_variant(); + + auto v = *result; + static_assert( + std::is_same_v>>); + HPX_TEST(hpx::holds_alternative>(v)); + + auto t = hpx::get>(v); + static_assert(std::is_same_v>); + + auto i = hpx::get<0>(t); + static_assert(std::is_same_v); + + HPX_TEST(i == 3); + } + + // tag_invoke overload + { + std::atomic start_called{false}; + std::atomic connect_called{false}; + std::atomic tag_invoke_overload_called{false}; + tt::sync_wait_with_variant(custom_sender2{custom_sender{ + start_called, connect_called, tag_invoke_overload_called}}); + HPX_TEST(!start_called); + HPX_TEST(!connect_called); + HPX_TEST(tag_invoke_overload_called); + } + + // Failure path + { + bool exception_thrown = false; + try + { + tt::sync_wait_with_variant(error_sender{}); + HPX_TEST(false); + } + catch (std::runtime_error const& e) + { + HPX_TEST_EQ(std::string(e.what()), std::string("error")); + exception_thrown = true; + } + HPX_TEST(exception_thrown); + } + + // cancellation path + { + auto result = + (stopped_sender_with_value_type{} | tt::sync_wait_with_variant()); + HPX_TEST(!result); // returned optional should be empty + } + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/execution_base/tests/include/algorithm_test_utils.hpp b/libs/core/execution_base/tests/include/algorithm_test_utils.hpp index 7db61a9a8d5b..73e7902cce09 100644 --- a/libs/core/execution_base/tests/include/algorithm_test_utils.hpp +++ b/libs/core/execution_base/tests/include/algorithm_test_utils.hpp @@ -410,6 +410,61 @@ struct custom_sender hpx::execution::experimental::set_error_t(std::exception_ptr)>; }; +struct custom_sender_multi_tuple +{ + std::atomic& start_called; + std::atomic& connect_called; + std::atomic& tag_invoke_overload_called; + + bool expect_set_value = true; + + template