Skip to content

Commit 95e16de

Browse files
authored
Add capy examples (#50)
* rename exec_statuscode.hpp to statuscode.hpp * add task example with boost.capy * add capy tbb example * update readme * check status in conditional variable wait
1 parent 9e03f65 commit 95e16de

12 files changed

Lines changed: 455 additions & 7 deletions

CMakeLists.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,28 @@ if(BUILD_TBB)
8282
endif()
8383
endif()
8484

85+
### capy options
86+
87+
option(BUILD_CAPY "Build the capy sources included in CoroutineTests" TRUE)
88+
89+
if (USE_SYSTEM_LIBS)
90+
set(USE_SYSTEM_CAPY_DEFAULT ON)
91+
else()
92+
set(USE_SYSTEM_CAPY_DEFAULT OFF)
93+
endif()
94+
option(USE_SYSTEM_CAPY
95+
"Pick up an existing installation of capy from the build environment"
96+
${USE_SYSTEM_CAPY_DEFAULT})
97+
unset(USE_SYSTEM_CAPY_DEFAULT)
98+
99+
if(BUILD_CAPY)
100+
if(USE_SYSTEM_CAPY)
101+
find_package(capy REQUIRED)
102+
else()
103+
add_subdirectory(extern/capy)
104+
endif()
105+
endif()
106+
85107
### CUDA options
86108

87109
option(BUILD_CUDA "Build with CUDA support" OFF)

examples/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ if(BUILD_BEMAN_TASK)
5656
endif()
5757
endif()
5858

59+
60+
if(BUILD_CAPY)
61+
add_executable(capy_task capy_task.cpp)
62+
target_link_libraries(capy_task PRIVATE CoroutineTests Boost::capy)
63+
if (BUILD_TBB)
64+
add_executable(capy_tbb capy_tbb.cpp)
65+
target_link_libraries(capy_tbb PRIVATE CoroutineTests Boost::capy TBB::tbb)
66+
endif()
67+
endif()
68+
5969
if(BUILD_CUDA AND BUILD_TBB)
6070
add_executable(alien_reco alien_reco.cpp)
6171
target_link_libraries(alien_reco PRIVATE CoroutineTests CUDA::cudart TBB::tbb)

examples/README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ This example shows usage of `task` coroutine return type from future C++26 stand
102102

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

105-
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.
105+
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.
106106

107107
## Alien
108108

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

145145
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).
146146

147+
## Capy task
148+
149+
Link: [capy_task.cpp](capy_task.cpp)
150+
151+
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.
152+
153+
## Capy TBB
154+
155+
Link: [capy_tbb.cpp](capy_tbb.cpp)
156+
157+
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.
158+
147159
## Reconstruction
148160

149161
Link: [alien_reco.cpp](alien_reco.cpp) and [exec_reco.cpp](exec_reco.cpp)

examples/capy_task.cpp

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#include <boost/capy.hpp>
2+
#include <chrono>
3+
#include <condition_variable>
4+
#include <coroutine>
5+
#include <cstdlib>
6+
#include <mutex>
7+
#include <string_view>
8+
9+
#include "capy_timer.hpp" // TimerIoAwaitable
10+
#include "logging_utils.hpp" // log, format_name
11+
#include "statuscode.hpp" // StatusCodeImpl
12+
13+
namespace tools {
14+
struct Tag {
15+
static constexpr const char* name = "tools";
16+
};
17+
using StatusCode = StatusCodeImpl<Tag>;
18+
} // namespace tools
19+
20+
namespace algs {
21+
struct Tag {
22+
static constexpr const char* name = "algs";
23+
};
24+
using StatusCode = StatusCodeImpl<Tag>;
25+
} // namespace algs
26+
27+
template <boost::capy::Executor Ex>
28+
class VerboseExecutor {
29+
30+
public:
31+
VerboseExecutor(Ex& ex) : m_executor(&ex) {
32+
static_assert(boost::capy::Executor<VerboseExecutor<Ex>>,
33+
"VerboseExecutor should be a valid capy Executor");
34+
}
35+
36+
auto post(std::coroutine_handle<> h) const {
37+
log() << "executor posting new work item" << std::endl;
38+
m_executor->post(h);
39+
}
40+
auto dispatch(std::coroutine_handle<> h) const {
41+
log() << "executor dispatching new work item" << std::endl;
42+
return m_executor->dispatch(h);
43+
}
44+
auto& context() const noexcept { return m_executor->context(); }
45+
auto on_work_started() const noexcept {
46+
log() << "executor work started" << std::endl;
47+
m_executor->on_work_started();
48+
}
49+
auto on_work_finished() const noexcept {
50+
log() << "executor work finished" << std::endl;
51+
m_executor->on_work_finished();
52+
}
53+
bool operator==(const VerboseExecutor& other) const noexcept {
54+
return (*m_executor) == (*other.m_executor);
55+
}
56+
57+
private:
58+
Ex* m_executor;
59+
};
60+
61+
boost::capy::task<tools::StatusCode> tool1_execute(std::string_view parent) {
62+
const auto self = format_name(parent, "tool1");
63+
64+
log(self) << "Calling async API in tool1" << std::endl;
65+
auto status = co_await TimerIoAwaitable{std::chrono::milliseconds(100),
66+
timer::StatusCode::SUCCESS, self};
67+
log(self) << "Result from async API in tool1: " << status << std::endl;
68+
69+
log(self) << "Finishing tool1" << std::endl;
70+
co_return tools::StatusCode::FAILURE;
71+
}
72+
73+
boost::capy::task<tools::StatusCode> tool2_execute(std::string_view parent) {
74+
const auto self = format_name(parent, "tool2");
75+
76+
log(self) << "Calling async API in tool2" << std::endl;
77+
auto status1 = co_await TimerIoAwaitable{std::chrono::milliseconds(10),
78+
timer::StatusCode::SUCCESS, self};
79+
log(self) << "Result from async API in tool2: " << status1 << std::endl;
80+
81+
log(self) << "Launching tool1" << std::endl;
82+
auto code = co_await tool1_execute(self);
83+
log(self) << "Result from tool1: " << code << std::endl;
84+
85+
log(self) << "Calling async API in tool2" << std::endl;
86+
auto status2 = co_await TimerIoAwaitable{std::chrono::milliseconds(10),
87+
timer::StatusCode::FAILURE, self};
88+
log(self) << "Result from async API in tool2: " << status2 << std::endl;
89+
90+
log(self) << "Finishing tool2" << std::endl;
91+
co_return tools::StatusCode::SUCCESS;
92+
}
93+
94+
boost::capy::task<tools::StatusCode> tool3_execute(std::string_view parent) {
95+
const auto self = format_name(parent, "tool3");
96+
log(self) << "Finishing tool3" << std::endl;
97+
co_return tools::StatusCode::FAILURE;
98+
}
99+
100+
boost::capy::task<algs::StatusCode> algorithm_execute(std::string_view parent) {
101+
const auto self = format_name(parent, "algorithm");
102+
103+
log(self) << "Calling async API in algorithm" << std::endl;
104+
auto status1 = co_await TimerIoAwaitable{std::chrono::milliseconds(42),
105+
timer::StatusCode::SUCCESS, self};
106+
log(self) << "Result from async API in algorithm: " << status1 << std::endl;
107+
108+
log(self) << "Launching tool1" << std::endl;
109+
auto code1 = co_await tool1_execute(self);
110+
log(self) << "Result from tool1: " << code1 << std::endl;
111+
112+
log(self) << "Calling async API in algorithm" << std::endl;
113+
auto status2 = co_await TimerIoAwaitable{std::chrono::milliseconds(17),
114+
timer::StatusCode::FAILURE, self};
115+
log(self) << "Result from async API in algorithm: " << status2 << std::endl;
116+
117+
log(self) << "Launching tool2" << std::endl;
118+
auto code2 = co_await tool2_execute(self);
119+
log(self) << "Result from tool2: " << code2 << std::endl;
120+
121+
log(self) << "Launching tool3" << std::endl;
122+
auto code3 = co_await tool3_execute(self);
123+
log(self) << "Result from tool3: " << code3 << std::endl;
124+
125+
log(self) << "Finishing algorithm" << std::endl;
126+
co_return algs::StatusCode::SUCCESS;
127+
}
128+
129+
int main() {
130+
log() << "main Starting" << std::endl;
131+
132+
auto pool = boost::capy::thread_pool(4);
133+
auto pool_executor = pool.get_executor();
134+
auto verbose_executor = VerboseExecutor(pool_executor);
135+
136+
auto condition = std::condition_variable();
137+
auto mutex = std::mutex();
138+
auto final_result = algs::StatusCode{};
139+
auto result_handler = [&condition, &mutex,
140+
&final_result](algs::StatusCode code) {
141+
{
142+
std::lock_guard lock(mutex);
143+
final_result = code;
144+
}
145+
condition.notify_one();
146+
};
147+
148+
log() << "main launching algorithm" << std::endl;
149+
boost::capy::run_async(verbose_executor,
150+
result_handler)(algorithm_execute("main"));
151+
152+
{
153+
log() << "main waiting for algorithm to finish..." << std::endl;
154+
auto lock = std::unique_lock(mutex);
155+
condition.wait(lock, [&final_result]() {
156+
return final_result != algs::StatusCode::UNDEFINED;
157+
});
158+
}
159+
log() << "Final status of algorithm " << final_result << std::endl;
160+
161+
log() << "main Done" << std::endl;
162+
return EXIT_SUCCESS;
163+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include <tbb/task_arena.h>
2+
3+
#include <boost/capy.hpp>
4+
#include <coroutine>
5+
6+
class TaskArenaContext : public boost::capy::execution_context {
7+
public:
8+
TaskArenaContext(tbb::task_arena& arena) : m_arena(&arena) {}
9+
10+
void schedule(std::coroutine_handle<> h) const {
11+
m_arena->enqueue([h]() { h.resume(); });
12+
}
13+
bool operator==(const TaskArenaContext& other) const noexcept {
14+
return m_arena == other.m_arena;
15+
}
16+
17+
private:
18+
tbb::task_arena* m_arena;
19+
};
20+
21+
class TaskArenaExecutor {
22+
23+
public:
24+
TaskArenaExecutor(TaskArenaContext& context) noexcept
25+
: m_context(&context) {
26+
static_assert(boost::capy::Executor<TaskArenaExecutor>,
27+
"TaskArenaExecutor should be a valid capy Executor");
28+
}
29+
30+
TaskArenaExecutor(TaskArenaExecutor const&) noexcept = default;
31+
32+
std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const {
33+
m_context->schedule(h);
34+
return std::noop_coroutine();
35+
}
36+
void post(std::coroutine_handle<> h) const { m_context->schedule(h); }
37+
TaskArenaContext& context() const noexcept { return *m_context; }
38+
void on_work_started() const noexcept {}
39+
void on_work_finished() const noexcept {}
40+
bool operator==(const TaskArenaExecutor& other) const noexcept {
41+
return m_context == other.m_context;
42+
}
43+
44+
private:
45+
TaskArenaContext* m_context;
46+
};

0 commit comments

Comments
 (0)