Skip to content

Commit 7656cc3

Browse files
authored
add example obtaining current scheduler from within alien coroutine (#61)
1 parent 96ef496 commit 7656cc3

4 files changed

Lines changed: 93 additions & 0 deletions

File tree

examples/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ add_executable(alien_counting_scope alien_counting_scope.cpp)
3636
target_link_libraries(alien_counting_scope PRIVATE CoroutineTests)
3737
add_executable(alien_schedule_on alien_schedule_on.cpp)
3838
target_link_libraries(alien_schedule_on PRIVATE CoroutineTests)
39+
add_executable(alien_get_scheduler alien_get_scheduler.cpp)
40+
target_link_libraries(alien_get_scheduler PRIVATE CoroutineTests)
3941

4042
if(BUILD_STDEXEC)
4143
add_executable(exec_task_stdexec exec_task.cpp)

examples/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ Link: [alien_schedule_on.cpp](alien_schedule_on.cpp)
150150

151151
This example demonstrates changing scheduler used by part of a chain of coroutines following the semantics from ["Alien" example](#alien). The `schedule_on` algorithm can be used to adapt a coroutine to use a different scheduler than its parent. Starting the coroutine suspends its parent and reschedule work on the new scheduler, finishing the coroutine reschedules continuation of its parent using parent's scheduler.
152152

153+
## Alien get_scheduler
154+
155+
Link: [alien_get_scheduler.cpp](alien_get_scheduler.cpp)
156+
157+
This example shows how the contents of promise type can be extracted from within a coroutine. The `GetScheduler` awaitable when co_awaited accessed the promise type and returns the currently used coroutine. The example uses then `schedule_on` algorithm from ["alien schedule_on" example](#alien-schedule_on) to alternate between used schedulers in each nested coroutine.
158+
153159
## Capy task
154160

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

examples/alien_get_scheduler.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#include <format>
2+
#include <string_view>
3+
4+
#include "CoroutineTests/alien/get_scheduler.hpp"
5+
#include "CoroutineTests/alien/schedule_on.hpp"
6+
#include "CoroutineTests/alien/sync_wait.hpp"
7+
#include "CoroutineTests/alien/tool.hpp"
8+
#include "CoroutineTests/threadpool.hpp"
9+
#include "logging_utils.hpp"
10+
11+
using namespace CoroutineTests::alien;
12+
using scheduler_t = std::function<void(std::coroutine_handle<>)>;
13+
tool::Task<void> innermost_task(std::string_view parent) {
14+
const auto self = format_name(parent, "innermost_task");
15+
log(self) << "Running innermost task" << std::endl;
16+
co_return;
17+
}
18+
19+
tool::Task<void> inner_task(scheduler_t innermost_scheduler,
20+
std::string_view parent) {
21+
const auto self = format_name(parent, "inner_task");
22+
log(self) << "Running inner task, scheduling innermost task on innermost "
23+
"scheduler"
24+
<< std::endl;
25+
co_await schedule_on(innermost_scheduler, innermost_task(self));
26+
log(self) << "Innermost task completed, resuming inner task" << std::endl;
27+
co_return;
28+
}
29+
30+
tool::Task<void> outer_task(scheduler_t inner_scheduler,
31+
std::string_view parent) {
32+
const auto self = format_name(parent, "outer_task");
33+
log(self) << "Running outer task" << std::endl;
34+
auto current_scheduler = co_await GetScheduler{};
35+
log(self) << "Got current scheduler in outer task, scheduling inner task "
36+
"on inner scheduler"
37+
<< std::endl;
38+
co_await schedule_on(inner_scheduler, inner_task(current_scheduler, self));
39+
log(self) << "Inner task completed, resuming outer task" << std::endl;
40+
co_return;
41+
}
42+
43+
int main() {
44+
log() << "Starting main, launching task..." << std::endl;
45+
auto threadpool_A = CoroutineTests::Threadpool(1);
46+
auto scheduler_A = [&](std::coroutine_handle<> h) {
47+
threadpool_A.enqueue_task(h);
48+
};
49+
50+
auto threadpool_B = CoroutineTests::Threadpool(1);
51+
auto scheduler_B = [&](std::coroutine_handle<> h) {
52+
threadpool_B.enqueue_task(h);
53+
};
54+
55+
sync_wait(scheduler_A, outer_task(scheduler_B, "main"));
56+
log() << "Finished waiting for task" << std::endl;
57+
return 0;
58+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <coroutine>
2+
#include <functional>
3+
4+
namespace CoroutineTests::alien {
5+
6+
class GetScheduler {
7+
public:
8+
// Don't skip suspension, we need to get the scheduler from the promise.
9+
bool await_ready() const noexcept { return false; }
10+
11+
// On suspend, capture the handle to the current coroutine and get the
12+
// scheduler from the promise. Then resume the coroutine immediately.
13+
template <typename PromiseType>
14+
std::coroutine_handle<> await_suspend(
15+
std::coroutine_handle<PromiseType> h) noexcept {
16+
m_scheduler = h.promise().get_scheduler();
17+
return h;
18+
}
19+
// On resume, return the captured scheduler.
20+
auto await_resume() const noexcept { return m_scheduler; }
21+
22+
private:
23+
// Storage for the captured scheduler.
24+
std::function<void(std::coroutine_handle<>)> m_scheduler;
25+
};
26+
27+
} // namespace CoroutineTests::alien

0 commit comments

Comments
 (0)