Skip to content

Remove experimental async interface - #2794

Open
SergeyKopienko wants to merge 17 commits into
mainfrom
dev/skopienko/remove_experimental_async
Open

SergeyKopienko wants to merge 17 commits into
mainfrom
dev/skopienko/remove_experimental_async

Conversation

@SergeyKopienko

@SergeyKopienko SergeyKopienko commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR removes the experimental asynchronous algorithms interface (oneapi::dpl::experimental::*_async, wait_for_all) together with the internal machinery that existed only to support it: the __future class and the legacy __result_and_scratch_storage container.

Since the async API was the only user-visible feature that could return a handle to an incomplete computation, dropping it lets the backend return plain sycl::event / std::tuple values instead of wrapping them into __future, and replaces the type-erased shared_ptr<__result_and_scratch_storage_base> lifetime management with value-semantics storage types.

What is removed

Public, experimental API (all in oneapi::dpl::experimental):

  • <oneapi/dpl/async> header
  • copy_async, fill_async, for_each_async, reduce_async, sort_async, inclusive_scan_async, exclusive_scan_async, transform_async, transform_reduce_async, transform_inclusive_scan_async, transform_exclusive_scan_async
  • wait_for_all

Internal entities:

  • __future, __create_future()
  • __checked_deferrable_wait()
  • __result_and_scratch_storage, __result_and_scratch_storage_base, __copyable_storage_state, __combined_storage::__move_state()
  • async_extension_defs.h, async_impl/async_impl_hetero.h, async_impl/glue_async_impl.h
  • The _ONEDPL_ASYNC_FORWARD_DECLARED hook in <oneapi/dpl/execution>

Backend changes

  • Introduced __finalize_sycl_call() in parallel_backend_sycl_utils.h as the single point where a pattern result is waited for. It is dispatched on a wait-mode tag and has two overloads: one for a bare sycl::event, and one for a tuple whose first element is a sycl::event. The tuple overload consults the __wait_required_of_finalize_sycl_call trait and forces __sync_mode when the tuple carries a storage type whose value must be read on the host, regardless of the requested mode.
  • Replaced __future::wait() / __future::get() / __checked_deferrable_wait() call sites with __finalize_sycl_call() plus an explicit __load_result() / __copy_result() read.
  • Replaced all sycl::event::wait() calls with sycl::event::wait_and_throw(), including in experimental/kt/single_pass_scan.h. This makes asynchronous SYCL exceptions consistently observable at the call site instead of being silently swallowed.
  • __parallel_for, __parallel_copy_impl, __parallel_histogram, __parallel_radix_sort and __parallel_partial_sort now return a plain sycl::event.
  • __parallel_transform_reduce, __parallel_transform_scan, __parallel_merge, __parallel_stable_sort, __merge_sort and the reduce/scan-by-segment helpers now return a std::tuple built on the new __device_storage / __result_storage / __combined_storage types, which own their memory directly.
  • Merge and merge-sort split-point storage is expressed with the new _split_points_device_storage_t aliases and the __parallel_merge_return_data_t helper, replacing manual new + shared_ptr lifetime extension. Split-point buffers are now value types moved out of the submitters.
  • _OutSizeLimit template parameters were converted from std::true_type / std::false_type to plain bool.
  • Added oneapi::dpl::__internal::__difference_tuple_t in utils_ranges.h and reused it in merge and set operations instead of spelling out tuples of __difference_t.

Documentation

  • Removed documentation/library_guide/parallel_api/async_api.rst and its entry in documentation/library_guide/guide_toc.rst.
  • Removed the paragraphs about the experimental async algorithms from rfcs/archived/asynchronous_api_general/readme.md.
  • Historical mentions in documentation/CHANGES.rst and documentation/release_notes.rst are left untouched.

Tests

  • Removed test/parallel_api/experimental/asynch.pass.cpp and asynch-scan.pass.cpp.
  • Removed test/general/header_inclusion_order_async_0.pass.cpp and header_inclusion_order_async_1.pass.cpp.
  • Dropped <oneapi/dpl/async> from the multiple-translation-units header umbrella.

Behavior impact

  • Code that used the oneapi::dpl::experimental::*_async functions, wait_for_all, or <oneapi/dpl/async> will no longer compile. These interfaces were experimental and are not part of the oneDPL specification.
  • Switching from wait() to wait_and_throw() means an exception reported to the SYCL queue's async handler is now rethrown at the point where the algorithm waits, instead of being ignored there.

Implementation details

Instead of __future structure, now we operate by std::tuple<sycl::event, .....> structure and introduce new __finalize_sycl_call() function for finalize each sycl call:

// Tag __async_mode describe a pattern call mode which should be executed asynchronously
struct __async_mode
{
};
// Tag __sync_mode describe a pattern call mode which should be executed synchronously
struct __sync_mode
{
};
// Tag __deferrable_mode describe a pattern call mode which should be executed
// synchronously/asynchronously : it's depends on ONEDPL_ALLOW_DEFERRED_WAITING macro state
struct __deferrable_mode
{
};

template <typename _WaitModeTag = __sync_mode, typename _Event>
std::enable_if_t<std::is_same_v<std::decay_t<_Event>, sycl::event>>
__finalize_sycl_call(_Event&& __event)
{
    if constexpr (std::is_same_v<_WaitModeTag, __async_mode>)
    {
        // no op
    }
    else if constexpr (std::is_same_v<_WaitModeTag, __sync_mode>)
    {
        __event.wait_and_throw();
    }
    else if constexpr (std::is_same_v<_WaitModeTag, __deferrable_mode>)
    {
#    if !ONEDPL_ALLOW_DEFERRED_WAITING
        __event.wait_and_throw();
#    endif
    }
    else
    {
        static_assert(sizeof(_WaitModeTag) == 0, "Unknown _WaitModeTag");
    }
}

template <typename... _Args>
constexpr bool
__is_wait_required_of_finalize_sycl_call()
{
    return (__wait_required_of_finalize_sycl_call<std::decay_t<_Args>>::value || ...);
}

template <typename _WaitModeTag = __sync_mode, template <typename...> typename _Tuple, typename... _Args>
void
__finalize_sycl_call(_Tuple<_Args...>& __tuple)
{
    static_assert(std::is_same_v<sycl::event, std::decay_t<std::tuple_element_t<0, _Tuple<_Args...>>>>,
                  "The first element of the tuple must be sycl::event");

    if constexpr (__is_wait_required_of_finalize_sycl_call<_Args...>())
    {
        // The result has to be read on the host, so a synchronous wait is required
        __finalize_sycl_call<__sync_mode>(std::get<0>(__tuple));
    }
    else
    {
        __finalize_sycl_call<_WaitModeTag>(std::get<0>(__tuple));
    }
}

Example:

    auto __res = __par_backend_hetero::__parallel_merge<true /*out size limit*/>(
        _BackendTag{}, ::std::forward<_ExecutionPolicy>(__exec),
        oneapi::dpl::__ranges::__get_subscription_view(std::forward<_Range1>(__rng1)),
        oneapi::dpl::__ranges::__get_subscription_view(std::forward<_Range2>(__rng2)),
        oneapi::dpl::__ranges::__get_subscription_view(std::forward<_Range3>(__rng3)), __comp, __proj1, __proj2);
    oneapi::dpl::__par_backend_hetero::__finalize_sycl_call(__res);

    const auto __stop_pos = oneapi::dpl::__par_backend_hetero::__load_result(std::get<1>(__res));

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Removes the experimental asynchronous API and simplifies SYCL backend synchronization and storage ownership.

Changes:

  • Deletes async APIs, documentation, and tests.
  • Replaces internal futures with events and owned storage tuples.
  • Makes device algorithm completion and exception handling explicit.

Reviewed changes

Copilot reviewed 33 out of 33 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/parallel_api/experimental/asynch.pass.cpp Removes async algorithm tests.
test/parallel_api/experimental/asynch-scan.pass.cpp Removes async scan tests.
test/general/multiple_translation_units/header_umbrella.h Removes async umbrella inclusion.
test/general/header_inclusion_order_async_1.pass.cpp Removes async inclusion-order test.
test/general/header_inclusion_order_async_0.pass.cpp Removes async inclusion-order test.
include/oneapi/dpl/pstl/utils.h Removes the future declaration.
include/oneapi/dpl/pstl/utils_ranges.h Adds a reusable difference tuple alias.
include/oneapi/dpl/pstl/onedpl_config.h Removes deferred-wait configuration.
include/oneapi/dpl/pstl/hetero/numeric_ranges_impl_hetero.h Explicitly waits and loads numeric results.
include/oneapi/dpl/pstl/hetero/numeric_impl_hetero.h Replaces future operations with event waits.
include/oneapi/dpl/pstl/hetero/histogram_impl_hetero.h Uses throwing event waits.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl.h Converts backend futures to events and storage tuples.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_utils.h Removes future and legacy storage machinery.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_reduce.h Returns reduction events with owned storage.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_reduce_then_scan_pos_tools.h Reuses the difference tuple alias.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_reduce_by_segment.h Propagates asynchronous exceptions.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_radix_sort.h Returns plain events.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_merge.h Introduces value-owned merge storage.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_merge_sort.h Value-owns split-point storage.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_histogram.h Returns plain histogram events.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_fpga.h Returns plain FPGA events.
include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_for.h Returns plain parallel-for events.
include/oneapi/dpl/pstl/hetero/algorithm_ranges_impl_hetero.h Makes range algorithms explicitly blocking.
include/oneapi/dpl/pstl/hetero/algorithm_impl_hetero.h Removes wait modes and loads results directly.
include/oneapi/dpl/internal/binary_search_impl.h Uses throwing event waits.
include/oneapi/dpl/internal/async_impl/glue_async_impl.h Removes public async implementations.
include/oneapi/dpl/internal/async_impl/async_impl_hetero.h Removes internal async patterns.
include/oneapi/dpl/internal/async_extension_defs.h Removes async API declarations.
include/oneapi/dpl/experimental/kt/single_pass_scan.h Propagates asynchronous scan errors.
include/oneapi/dpl/execution Stops loading async implementations.
include/oneapi/dpl/async Removes the async public header.
documentation/library_guide/parallel_api/async_api.rst Removes async API documentation.
documentation/library_guide/macros.rst Removes deferred-wait macro documentation.
Suppressed comments (1)

documentation/library_guide/parallel_api/async_api.rst:1

  • Removing this page leaves documentation/library_guide/guide_toc.rst:22 pointing at /library_guide/parallel_api/async_api. Sphinx will report a missing toctree document and the Technology Preview navigation remains stale. Remove that toctree entry as part of this deletion.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 34 out of 34 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

documentation/library_guide/parallel_api/async_api.rst:1

  • Deleting this page leaves /library_guide/parallel_api/async_api referenced by documentation/library_guide/guide_toc.rst:22. Sphinx will report a missing toctree document, so remove that toctree entry as part of this removal.
    rfcs/archived/asynchronous_api_general/readme.md:173
  • Removing this section also removes the #onedpl-experimental-asynchronous-algorithms anchor, but the introduction at line 14 still links to that anchor. Update or remove that link (or retain an anchor explaining the API's removal) to avoid a broken internal RFC link.
### C++ async & future

Comment thread include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl.h Outdated
@SergeyKopienko
SergeyKopienko force-pushed the dev/skopienko/remove_experimental_async branch from ba54c80 to f8aa159 Compare August 14, 2026 14:21
@SergeyKopienko
SergeyKopienko marked this pull request as draft August 14, 2026 14:57
@SergeyKopienko
SergeyKopienko force-pushed the dev/skopienko/remove_experimental_async branch 11 times, most recently from dae98ba to 644966d Compare August 17, 2026 11:57
@SergeyKopienko
SergeyKopienko requested a balanced review from Copilot August 17, 2026 12:13
@SergeyKopienko
SergeyKopienko marked this pull request as ready for review August 17, 2026 12:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

include/oneapi/dpl/experimental/kt/single_pass_scan.h:304

  • If wait_and_throw() reports an asynchronous exception, control leaves before sycl::free, leaking the malloc_device allocation. Free the temporary allocation on both success and exception paths (preferably with RAII).
        __prev_event.wait_and_throw();
        sycl::free(__device_mem, __queue);

include/oneapi/dpl/experimental/kt/single_pass_scan.h:303

  • The PR states that all sycl::event::wait() calls are replaced so asynchronous exceptions are consistently observable, but core_resource_backend::async_waiter::wait() still calls my_end_event.wait() in include/oneapi/dpl/internal/dynamic_selection_impl/sycl_backend.h:102-105. Convert that remaining event wait to wait_and_throw(), or narrow the stated behavior if dynamic-selection waits are intentionally excluded.
        __prev_event.wait_and_throw();

Comment thread include/oneapi/dpl/pstl/hetero/numeric_impl_hetero.h Outdated
@SergeyKopienko
SergeyKopienko requested a balanced review from Copilot August 17, 2026 12:56
@SergeyKopienko
SergeyKopienko force-pushed the dev/skopienko/remove_experimental_async branch from a53651d to b991297 Compare August 17, 2026 13:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated no new comments.

Suppressed comments (6)

include/oneapi/dpl/pstl/hetero/algorithm_impl_hetero.h:524

  • This no-argument __load_result requires the result tuple—and therefore the input value type—to be default-constructible. minmax_element has no such value-type requirement, so valid non-default-constructible element types regress from the previous future-based extraction. Use result extraction that copy-constructs from storage.
    const _ReduceValueType __result = __load_result(std::get<1>(__res));

include/oneapi/dpl/pstl/hetero/algorithm_ranges_impl_hetero.h:1654

  • This extraction default-constructs _ReduceValueType, which embeds two copies of the range value type. Device ranges minmax_element therefore rejects valid non-default-constructible values; the removed future extraction did not impose that requirement. Use construction-based result extraction instead.
    [[maybe_unused]] auto [__idx_min, __idx_max, __min, __max] = __load_result(std::get<1>(__res));

include/oneapi/dpl/pstl/hetero/algorithm_impl_hetero.h:469

  • __load_result(storage) is enabled only when _ReduceValueType is default-constructible. Because this reduction tuple embeds the iterator's value type, min_element now fails for non-default-constructible value types that the algorithm otherwise supports; the removed future path copy-constructed the stored value instead. Extract the result without first default-constructing the tuple.

This issue also appears on line 524 of the same file.

    const _ReduceValueType __result = __load_result(std::get<1>(__res));

include/oneapi/dpl/pstl/hetero/algorithm_ranges_impl_hetero.h:1575

  • The ranges reduction tuple contains the range value type, but this overload of __load_result is constrained to default-constructible tuples. Consequently, device ranges min/min_element no longer compile for a non-default-constructible value type, although those APIs do not require one. Restore construction-based extraction from storage.

This issue also appears on line 1654 of the same file.

    [[maybe_unused]] auto [__idx, __val] = __load_result(std::get<1>(__res));

include/oneapi/dpl/pstl/hetero/dpcpp/parallel_backend_sycl_utils.h:792

  • The claimed conversion of all sycl::event::wait() calls is incomplete: include/oneapi/dpl/internal/dynamic_selection_impl/sycl_backend.h:87-104 stores a sycl::event and still calls my_end_event.wait(). Async exceptions in that backend remain unobservable at the wait site, contrary to the stated behavior; convert that call too or narrow the PR's behavior claim.
    else if constexpr (std::is_same_v<_WaitModeTag, __sync_mode>)
    {
        __event.wait_and_throw();
    }

include/oneapi/dpl/experimental/kt/single_pass_scan.h:304

  • If wait_and_throw() reports an asynchronous exception, control skips sycl::free, leaking the raw device allocation. Free the allocation on both success and exception paths before propagating the error.
        __prev_event.wait_and_throw();
        sycl::free(__device_mem, __queue);

@SergeyKopienko
SergeyKopienko force-pushed the dev/skopienko/remove_experimental_async branch 3 times, most recently from 411ab03 to 0229e89 Compare August 17, 2026 15:42
@akukanov akukanov modified the milestone: 2022.15.0 Aug 17, 2026
@SergeyKopienko
SergeyKopienko force-pushed the dev/skopienko/remove_experimental_async branch 2 times, most recently from 5b069de to cbb8acd Compare August 27, 2026 07:54
…remove struct __copyable_storage_state + struct __combined_storage::__move_state() as unused anymore
… fix compile error: non-constant-expression cannot be narrowed from type 'long' to 'std::size_t' (aka 'unsigned long') in initializer list [-Wc++11-narrowing]
…elf review comment: remove extra [[maybe_unused]] in __pattern_minmax_element_impl()
…elf review comment: remove extra [[maybe_unused]] in __pattern_min_element_impl()
…fix self review comment: remove __is_wait_required_on_finalize_sycl_call()
…ro::__parallel_for() calls instead of using auto
@SergeyKopienko
SergeyKopienko force-pushed the dev/skopienko/remove_experimental_async branch from cbb8acd to 468fd05 Compare August 31, 2026 10:32

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants