Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/ci.cpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ jobs:
include:
- { name: "CPU (clang 16, Debug)", build: "Debug", tag: llvm16-cuda12.9, cxxstd: "20", cxxflags: "-stdlib=libc++" }
- { name: "CPU (clang 16, Debug, c++23)", build: "Debug", tag: llvm16-cuda12.9, cxxstd: "23", cxxflags: "-stdlib=libc++" }
- { name: "CPU (clang 16, Debug, TSAN)", build: "Debug", tag: llvm16-cuda12.9, cxxstd: "20", cxxflags: "-fsanitize=thread" }
- { name: "CPU (clang 16, Release)", build: "Release", tag: llvm16-cuda12.9, cxxstd: "20", cxxflags: "-stdlib=libc++" }
- { name: "CPU (clang 16, Release, ASAN)", build: "Release", tag: llvm16-cuda12.9, cxxstd: "20", cxxflags: "-stdlib=libc++ -fsanitize=address -fsanitize-ignorelist=/home/coder/stdexec/sanitizer-ignorelist.txt" }
- { name: "CPU (gcc 11, Debug)", build: "Debug", tag: gcc11-cuda12.9, cxxstd: "20", cxxflags: "", }
- { name: "CPU (gcc 11, Release)", build: "Release", tag: gcc11-cuda12.9, cxxstd: "20", cxxflags: "", }
- { name: "CPU (gcc 11, Release, ASAN)", build: "Release", tag: gcc11-cuda12.9, cxxstd: "20", cxxflags: "-fsanitize=address" }
- { name: "CPU (gcc 12, Release, TSAN)", build: "Release", tag: gcc12-cuda12.9, cxxstd: "20", cxxflags: "-fsanitize=thread" }
- { name: "CPU (gcc 13, Debug)", build: "Release", tag: gcc13-cuda12.9, cxxstd: "20", cxxflags: "", }
- { name: "CPU (gcc 13, Debug)", build: "Debug", tag: gcc13-cuda12.9, cxxstd: "20", cxxflags: "", }
- { name: "CPU (gcc 14, Debug)", build: "Debug", tag: gcc14-cuda12.9, cxxstd: "20", cxxflags: "", }
- { name: "CPU (gcc 14, Debug, ASAN)", build: "Debug", tag: gcc14-cuda12.9, cxxstd: "20", cxxflags: "-fsanitize=address" }
- { name: "CPU (gcc 14, Debug, TSAN)", build: "Debug", tag: gcc14-cuda12.9, cxxstd: "20", cxxflags: "-fsanitize=thread" }
- { name: "CPU (gcc 14, Release, LEAK)", build: "Release", tag: gcc14-cuda12.9, cxxstd: "20", cxxflags: "-fsanitize=leak", }
- { name: "CPU (gcc 14, Release, c++23)", build: "Release", tag: gcc14-cuda12.9, cxxstd: "23", cxxflags: "", }
container:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ dependencies and only requires a sufficiently new compiler:

- gcc 11+
- clang 16+
- MSVC 14.43+
- XCode 16+
- [nvc++ 25.9+](https://developer.nvidia.com/nvidia-hpc-sdk-releases) (required for [GPU
support](#gpu-support)).
Expand Down
31 changes: 18 additions & 13 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ function(def_example example)
add_executable(${target} ${source})
target_link_libraries(${target}
PRIVATE STDEXEC::stdexec
stdexec_executable_flags)
stdexec_executable_flags
$<TARGET_NAME_IF_EXISTS:TBB::tbb>)
endfunction()

set(stdexec_examples
Expand Down Expand Up @@ -63,30 +64,34 @@ if (LINUX)
endif ()

foreach(example ${stdexec_examples})
def_example(${example})
def_example(${example})
endforeach()

if(STDEXEC_ENABLE_CUDA)
add_subdirectory(nvexec)
add_subdirectory(nvexec)
endif()

if (STDEXEC_ENABLE_TBB)
add_executable(example.benchmark.tbb_thread_pool benchmark/tbb_thread_pool.cpp)
target_link_libraries(example.benchmark.tbb_thread_pool PRIVATE STDEXEC::tbbexec)
add_executable(example.benchmark.tbb_thread_pool benchmark/tbb_thread_pool.cpp)
target_link_libraries(example.benchmark.tbb_thread_pool PRIVATE STDEXEC::tbbexec)

add_executable(example.benchmark.tbb_thread_pool_nested benchmark/tbb_thread_pool_nested.cpp)
target_link_libraries(example.benchmark.tbb_thread_pool_nested PRIVATE STDEXEC::tbbexec)
add_executable(example.benchmark.tbb_thread_pool_nested benchmark/tbb_thread_pool_nested.cpp)
target_link_libraries(example.benchmark.tbb_thread_pool_nested PRIVATE STDEXEC::tbbexec)

add_executable(example.benchmark.fibonacci benchmark/fibonacci.cpp)
target_link_libraries(example.benchmark.fibonacci PRIVATE STDEXEC::tbbexec)
add_executable(example.benchmark.fibonacci benchmark/fibonacci.cpp)
target_link_libraries(example.benchmark.fibonacci PRIVATE STDEXEC::tbbexec)
endif()

if(STDEXEC_ENABLE_TASKFLOW)
add_executable(example.benchmark.taskflow_thread_pool benchmark/taskflow_thread_pool.cpp)
target_link_libraries(example.benchmark.taskflow_thread_pool PRIVATE STDEXEC::taskflowexec)
add_executable(example.benchmark.taskflow_thread_pool benchmark/taskflow_thread_pool.cpp)
target_link_libraries(example.benchmark.taskflow_thread_pool
PRIVATE STDEXEC::taskflowexec
$<TARGET_NAME_IF_EXISTS:TBB::tbb>)
endif()

if(STDEXEC_ENABLE_ASIO)
add_executable(example.benchmark.asio_thread_pool benchmark/asio_thread_pool.cpp)
target_link_libraries(example.benchmark.asio_thread_pool PRIVATE STDEXEC::asioexec)
add_executable(example.benchmark.asio_thread_pool benchmark/asio_thread_pool.cpp)
target_link_libraries(example.benchmark.asio_thread_pool
PRIVATE STDEXEC::asioexec
$<TARGET_NAME_IF_EXISTS:TBB::tbb>)
endif()
56 changes: 30 additions & 26 deletions include/exec/at_coroutine_exit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,9 @@ namespace experimental::execution
public:
using promise_type = __promise;

#if STDEXEC_EDG()
__task(__std::coroutine_handle<__promise> __coro) noexcept
explicit(!STDEXEC_EDG()) __task(__std::coroutine_handle<__promise> __coro) noexcept
: __coro_(__coro)
{}
#else
explicit __task(__std::coroutine_handle<__promise> __coro) noexcept
: __coro_(__coro)
{}
#endif

__task(__task&& __that) noexcept
: __coro_(std::exchange(__that.__coro_, {}))
Expand All @@ -150,13 +144,22 @@ namespace experimental::execution
return false;
}

//! \brief Splice the cleanup action into the chain of continuations.
//! \param __parent The coroutine that is registering an action to be performed at
//! coroutine exit; i.e., the coroutine that is co_await-ing the result of calling
//! at_coroutine_exit.
template <__has_continuation _Promise>
auto await_suspend(__std::coroutine_handle<_Promise> __parent) noexcept -> bool
{
// Set the cleanup task's scheduler to the parent coroutine's scheduler.
__coro_.promise().__scheduler_ = get_scheduler(get_env(__parent.promise()));
// This causes the parent to be resumed after the cleanup action is performed.
__coro_.promise().set_continuation(__parent.promise().continuation());
// This causes the parent to invoke the cleanup action when it performs the final
// suspend. Also, the parent is now responsible for destroying the cleanup
// coroutine.
__parent.promise().set_continuation(__coro_);
return false;
return false; // i.e., do not suspend, call await_resume immediately
}

auto await_resume() noexcept -> std::tuple<_Ts&...>
Expand All @@ -172,13 +175,14 @@ namespace experimental::execution
return false;
}

static auto
await_suspend(__std::coroutine_handle<__promise> __h) noexcept -> __std::coroutine_handle<>
//! \param __h The coroutine created by __co_impl below.
static auto await_suspend(__std::coroutine_handle<__promise> __h) noexcept //
-> __std::coroutine_handle<>
{
__promise& __p = __h.promise();
auto __coro = __p.__is_unhandled_stopped_ ? __p.continuation().unhandled_stopped()
: __p.continuation().handle();
return STDEXEC_DESTROY_AND_CONTINUE(__h, __coro);
auto __cont = __h.promise().continuation();
auto __coro = __h.promise().__is_stopped_ ? __cont.unhandled_stopped() : __cont.handle();
__h.destroy();
return __coro;
}

void await_resume() const noexcept {}
Expand All @@ -197,23 +201,18 @@ namespace experimental::execution

struct __promise : with_awaitable_senders<__promise>
{
#if STDEXEC_EDG()
template <class _Action>
__promise(_Action&&, _Ts&&... __ts) noexcept
: __args_{__ts...}
{}
#else
template <class _Action>
explicit __promise(_Action&&, _Ts&... __ts) noexcept
explicit(!STDEXEC_EDG()) __promise(_Action&&, _Ts&... __ts) noexcept
: __args_{__ts...}
{}
#endif

[[nodiscard]]
auto initial_suspend() noexcept -> __std::suspend_always
{
return {};
}

[[nodiscard]]
auto final_suspend() noexcept -> __final_awaitable
{
return {};
Expand All @@ -227,41 +226,46 @@ namespace experimental::execution
std::terminate();
}

[[nodiscard]]
auto unhandled_stopped() noexcept -> __std::coroutine_handle<__promise>
{
__is_unhandled_stopped_ = true;
__is_stopped_ = true;
return __std::coroutine_handle<__promise>::from_promise(*this);
}

[[nodiscard]]
auto get_return_object() noexcept -> __task
{
return __task(__std::coroutine_handle<__promise>::from_promise(*this));
}

template <class _Awaitable>
[[nodiscard]]
auto await_transform(_Awaitable&& __awaitable) noexcept -> decltype(auto)
{
return as_awaitable(__die_on_stop(static_cast<_Awaitable&&>(__awaitable)), *this);
}

[[nodiscard]]
auto get_env() const noexcept -> __env
{
return {*this};
}

bool __is_unhandled_stopped_{false};
bool __is_stopped_{false};
std::tuple<_Ts&...> __args_{};
__any_scheduler_t __scheduler_{STDEXEC::inline_scheduler{}};
};

// __coro_ refers to the coroutine created by __co_impl below
__std::coroutine_handle<__promise> __coro_;
};

struct __at_coro_exit_t
{
private:
template <class _Action, class... _Ts>
static auto __impl(_Action __action, _Ts... __ts) -> __task<_Ts...>
static auto __co_impl(_Action __action, _Ts... __ts) -> __task<_Ts...>
{
co_await static_cast<_Action&&>(__action)(static_cast<_Ts&&>(__ts)...);
}
Expand All @@ -271,7 +275,7 @@ namespace experimental::execution
requires __callable<__decay_t<_Action>, __decay_t<_Ts>...>
auto operator()(_Action&& __action, _Ts&&... __ts) const -> __task<_Ts...>
{
return __impl(static_cast<_Action&&>(__action), static_cast<_Ts&&>(__ts)...);
return __co_impl(static_cast<_Action&&>(__action), static_cast<_Ts&&>(__ts)...);
}
};
} // namespace __at_coro_exit
Expand Down
16 changes: 8 additions & 8 deletions include/exec/on_coro_disposition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@ namespace experimental::execution
return false;
}

static auto
await_suspend(__std::coroutine_handle<__promise> __h) noexcept -> __std::coroutine_handle<>
static auto await_suspend(__std::coroutine_handle<__promise> __h) noexcept //
-> __std::coroutine_handle<>
{
__promise& __p = __h.promise();
auto __coro = __p.__is_unhandled_stopped_ ? __p.continuation().unhandled_stopped()
: __p.continuation().handle();
return STDEXEC_DESTROY_AND_CONTINUE(__h, __coro);
auto __cont = __h.promise().continuation();
auto __coro = __h.promise().__is_stopped_ ? __cont.unhandled_stopped() : __cont.handle();
__h.destroy();
return __coro;
}

void await_resume() const noexcept {}
Expand Down Expand Up @@ -174,7 +174,7 @@ namespace experimental::execution

auto unhandled_stopped() noexcept -> __std::coroutine_handle<__promise>
{
__is_unhandled_stopped_ = true;
__is_stopped_ = true;
return __std::coroutine_handle<__promise>::from_promise(*this);
}

Expand All @@ -200,7 +200,7 @@ namespace experimental::execution
return {*this};
}

bool __is_unhandled_stopped_{false};
bool __is_stopped_{false};
std::tuple<_Ts&...> __args_{};
using __get_disposition_callback_t = task_disposition (*)(void*) noexcept;
__std::coroutine_handle<> __parent_{};
Expand Down
Loading
Loading