Skip to content
Merged
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
22 changes: 22 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,28 @@ if(BUILD_TBB)
endif()
endif()

### capy options

option(BUILD_CAPY "Build the capy sources included in CoroutineTests" TRUE)

if (USE_SYSTEM_LIBS)
set(USE_SYSTEM_CAPY_DEFAULT ON)
else()
set(USE_SYSTEM_CAPY_DEFAULT OFF)
endif()
option(USE_SYSTEM_CAPY
"Pick up an existing installation of capy from the build environment"
${USE_SYSTEM_CAPY_DEFAULT})
unset(USE_SYSTEM_CAPY_DEFAULT)

if(BUILD_CAPY)
if(USE_SYSTEM_CAPY)
find_package(capy REQUIRED)
else()
add_subdirectory(extern/capy)
endif()
endif()

### CUDA options

option(BUILD_CUDA "Build with CUDA support" OFF)
Expand Down
10 changes: 10 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ if(BUILD_BEMAN_TASK)
endif()
endif()


if(BUILD_CAPY)
add_executable(capy_task capy_task.cpp)
target_link_libraries(capy_task PRIVATE CoroutineTests Boost::capy)
if (BUILD_TBB)
add_executable(capy_tbb capy_tbb.cpp)
target_link_libraries(capy_tbb PRIVATE CoroutineTests Boost::capy TBB::tbb)
endif()
endif()

if(BUILD_CUDA AND BUILD_TBB)
add_executable(alien_reco alien_reco.cpp)
target_link_libraries(alien_reco PRIVATE CoroutineTests CUDA::cudart TBB::tbb)
Expand Down
14 changes: 13 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ This example shows usage of `task` coroutine return type from future C++26 stand

Link: [exec_tbb.cpp](exec_tbb.cpp)

This is a variant of the "Exec task" example, but using a custom C++ senders/receivers scheduler to execute task coroutines on Intel TBB task arena.
This is a variant of the ["Exec task" example](#exec-task), but using a custom C++ senders/receivers scheduler to execute task coroutines on Intel TBB task arena.

## Alien

Expand Down Expand Up @@ -144,6 +144,18 @@ Link: [alien_counting_scope.cpp](alien_counting_scope.cpp)

This example demonstrates dynamic work submitting with `counting_scope` compatible with coroutine semantic as in ["Alien" example](#alien). `spawn` schedules execution of a coroutine that returns `void`. `join()` blocks the current thread until all submitted coroutines are finished (and rethrows the first exception captured from submitted work, if any).

## Capy task

Link: [capy_task.cpp](capy_task.cpp)

This is a variant of ["Exec task" example](#exec-task) using Boost.Capy and IoAwaitables protocol ([p4003](https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2026/p4003r0.pdf)) instead of C++26 execution.

## Capy TBB

Link: [capy_tbb.cpp](capy_tbb.cpp)

This is a variant of ["Exec tbb" example](#exec-tbb) using Boost.Capy and IoAwaitables protocol ([p4003](https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2026/p4003r0.pdf)) instead of C++26 execution. This example shows implementation and usage of custom executor scheduling tasks on TBB task arena.

## Reconstruction

Link: [alien_reco.cpp](alien_reco.cpp) and [exec_reco.cpp](exec_reco.cpp)
Expand Down
163 changes: 163 additions & 0 deletions examples/capy_task.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#include <boost/capy.hpp>
#include <chrono>
#include <condition_variable>
#include <coroutine>
#include <cstdlib>
#include <mutex>
#include <string_view>

#include "capy_timer.hpp" // TimerIoAwaitable
#include "logging_utils.hpp" // log, format_name
#include "statuscode.hpp" // StatusCodeImpl

namespace tools {
struct Tag {
static constexpr const char* name = "tools";
};
using StatusCode = StatusCodeImpl<Tag>;
} // namespace tools

namespace algs {
struct Tag {
static constexpr const char* name = "algs";
};
using StatusCode = StatusCodeImpl<Tag>;
} // namespace algs

template <boost::capy::Executor Ex>
class VerboseExecutor {

public:
VerboseExecutor(Ex& ex) : m_executor(&ex) {
static_assert(boost::capy::Executor<VerboseExecutor<Ex>>,
"VerboseExecutor should be a valid capy Executor");
}

auto post(std::coroutine_handle<> h) const {
log() << "executor posting new work item" << std::endl;
m_executor->post(h);
}
auto dispatch(std::coroutine_handle<> h) const {
log() << "executor dispatching new work item" << std::endl;
return m_executor->dispatch(h);
}
auto& context() const noexcept { return m_executor->context(); }
auto on_work_started() const noexcept {
log() << "executor work started" << std::endl;
m_executor->on_work_started();
}
auto on_work_finished() const noexcept {
log() << "executor work finished" << std::endl;
m_executor->on_work_finished();
}
bool operator==(const VerboseExecutor& other) const noexcept {
return (*m_executor) == (*other.m_executor);
}

private:
Ex* m_executor;
};

boost::capy::task<tools::StatusCode> tool1_execute(std::string_view parent) {
const auto self = format_name(parent, "tool1");

log(self) << "Calling async API in tool1" << std::endl;
auto status = co_await TimerIoAwaitable{std::chrono::milliseconds(100),
timer::StatusCode::SUCCESS, self};
log(self) << "Result from async API in tool1: " << status << std::endl;

log(self) << "Finishing tool1" << std::endl;
co_return tools::StatusCode::FAILURE;
}

boost::capy::task<tools::StatusCode> tool2_execute(std::string_view parent) {
const auto self = format_name(parent, "tool2");

log(self) << "Calling async API in tool2" << std::endl;
auto status1 = co_await TimerIoAwaitable{std::chrono::milliseconds(10),
timer::StatusCode::SUCCESS, self};
log(self) << "Result from async API in tool2: " << status1 << std::endl;

log(self) << "Launching tool1" << std::endl;
auto code = co_await tool1_execute(self);
log(self) << "Result from tool1: " << code << std::endl;

log(self) << "Calling async API in tool2" << std::endl;
auto status2 = co_await TimerIoAwaitable{std::chrono::milliseconds(10),
timer::StatusCode::FAILURE, self};
log(self) << "Result from async API in tool2: " << status2 << std::endl;

log(self) << "Finishing tool2" << std::endl;
co_return tools::StatusCode::SUCCESS;
}

boost::capy::task<tools::StatusCode> tool3_execute(std::string_view parent) {
const auto self = format_name(parent, "tool3");
log(self) << "Finishing tool3" << std::endl;
co_return tools::StatusCode::FAILURE;
}

boost::capy::task<algs::StatusCode> algorithm_execute(std::string_view parent) {
const auto self = format_name(parent, "algorithm");

log(self) << "Calling async API in algorithm" << std::endl;
auto status1 = co_await TimerIoAwaitable{std::chrono::milliseconds(42),
timer::StatusCode::SUCCESS, self};
log(self) << "Result from async API in algorithm: " << status1 << std::endl;

log(self) << "Launching tool1" << std::endl;
auto code1 = co_await tool1_execute(self);
log(self) << "Result from tool1: " << code1 << std::endl;

log(self) << "Calling async API in algorithm" << std::endl;
auto status2 = co_await TimerIoAwaitable{std::chrono::milliseconds(17),
timer::StatusCode::FAILURE, self};
log(self) << "Result from async API in algorithm: " << status2 << std::endl;

log(self) << "Launching tool2" << std::endl;
auto code2 = co_await tool2_execute(self);
log(self) << "Result from tool2: " << code2 << std::endl;

log(self) << "Launching tool3" << std::endl;
auto code3 = co_await tool3_execute(self);
log(self) << "Result from tool3: " << code3 << std::endl;

log(self) << "Finishing algorithm" << std::endl;
co_return algs::StatusCode::SUCCESS;
}

int main() {
log() << "main Starting" << std::endl;

auto pool = boost::capy::thread_pool(4);
auto pool_executor = pool.get_executor();
auto verbose_executor = VerboseExecutor(pool_executor);

auto condition = std::condition_variable();
auto mutex = std::mutex();
auto final_result = algs::StatusCode{};
auto result_handler = [&condition, &mutex,
&final_result](algs::StatusCode code) {
{
std::lock_guard lock(mutex);
final_result = code;
}
condition.notify_one();
};

log() << "main launching algorithm" << std::endl;
boost::capy::run_async(verbose_executor,
result_handler)(algorithm_execute("main"));

{
log() << "main waiting for algorithm to finish..." << std::endl;
auto lock = std::unique_lock(mutex);
condition.wait(lock, [&final_result]() {
return final_result != algs::StatusCode::UNDEFINED;
});
}
log() << "Final status of algorithm " << final_result << std::endl;

log() << "main Done" << std::endl;
return EXIT_SUCCESS;
}
46 changes: 46 additions & 0 deletions examples/capy_task_arena_executor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include <tbb/task_arena.h>

#include <boost/capy.hpp>
#include <coroutine>

class TaskArenaContext : public boost::capy::execution_context {
public:
TaskArenaContext(tbb::task_arena& arena) : m_arena(&arena) {}

void schedule(std::coroutine_handle<> h) const {
m_arena->enqueue([h]() { h.resume(); });
}
bool operator==(const TaskArenaContext& other) const noexcept {
return m_arena == other.m_arena;
}

private:
tbb::task_arena* m_arena;
};

class TaskArenaExecutor {

public:
TaskArenaExecutor(TaskArenaContext& context) noexcept
: m_context(&context) {
static_assert(boost::capy::Executor<TaskArenaExecutor>,
"TaskArenaExecutor should be a valid capy Executor");
}

TaskArenaExecutor(TaskArenaExecutor const&) noexcept = default;

std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const {
m_context->schedule(h);
return std::noop_coroutine();
}
void post(std::coroutine_handle<> h) const { m_context->schedule(h); }
TaskArenaContext& context() const noexcept { return *m_context; }
void on_work_started() const noexcept {}
void on_work_finished() const noexcept {}
bool operator==(const TaskArenaExecutor& other) const noexcept {
return m_context == other.m_context;
}

private:
TaskArenaContext* m_context;
};
Loading