I was trying to learn more about C++ 20 coroutines. After trying to read the documentation and watching few videos, I tried to write a timer that uses coroutines.
#include <coroutine>
#include <thread>
#include <chrono>
#include <utility>
#include <iostream>
struct Timer
{
struct promise_type
{
std::chrono::seconds m_wait_for_seconds;
void unhandled_exception() noexcept
{
}
std::suspend_never initial_suspend() noexcept
{
return {};
}
std::suspend_always final_suspend() noexcept
{
return {};
}
std::suspend_always await_transform(std::chrono::seconds value)
{
m_wait_for_seconds = value;
return {};
}
Timer get_return_object()
{
return Timer{*this};
}
};
using handle_type = std::coroutine_handle<promise_type>;
handle_type handle;
explicit Timer(promise_type& promise)
: handle(handle_type::from_promise(promise))
{
}
Timer(Timer&& other)
: handle(std::exchange(other.handle, nullptr))
{
}
~Timer()
{
if (handle)
{
handle.destroy();
}
}
void start()
{
if (!handle.done())
{
std::jthread t([this] {
std::this_thread::sleep_for(handle.promise().m_wait_for_seconds);
std::cout << "After waiting for " << handle.promise().m_wait_for_seconds.count() << " seconds\n";
});
}
}
};
Timer start_timer(std::chrono::seconds value)
{
co_await value;
}
int main()
{
Timer timer{start_timer(std::chrono::seconds(2))};
timer.start();
}
This code seems to work fine. Are there any ways to improve this?
1 Answer 1
This is pointless. You wrote a coroutine handler that only deals with a single timeout value, waits for that amount of time, and then it's finished. The actual coroutine, start_timer()
, can only co_await
a timeout value once. Why would you use this, instead of not using any coroutines and just calling std::this_thread::sleep_for()
when you need to sleep?
To be of any use, I would expect the coroutine handler to switch to another coroutine when the first one calls co_await
. The handler then becomes a scheduler. For example, it would be useful if you could write something like this:
Task blink_led(int led, std::chrono::seconds interval) {
while (true) {
std::println("led {} on", led);
co_await Delay(interval);
std::println("led {} off", led);
co_await Delay(interval);
}
}
int main() {
Scheduler scheduler;
scheduler.addTask(blink_led(1, std::chrono::seconds(1)));
scheduler.addTask(blink_led(2, std::chrono::seconds(2)));
scheduler.run();
}
Here Task
is a coroutine type, when it co_await
s a Delay
, the Scheduler
should switch to another task that has something to do, or sleep until one of the delays times out.
-
\$\begingroup\$ Can you please elaborate more? What does Delay contain and how Scheduler switches to another task when co_await is triggered? Is Scheduler also a coroutine? \$\endgroup\$kiner_shah– kiner_shah2025年05月11日 07:18:32 +00:00Commented May 11 at 7:18
-
1\$\begingroup\$ I'm not saying how you should implement
Scheduler
andDelay
, I'm just giving an example of much more realistic code using a hypothetical scheduler than yourmain()
. I'm sorry for saying this, but you basically only drew two circles and then asked us to review your owl. We don't expect a masterpiece, but you'll get a much better review of your code if it actually does something useful and non-trivial. \$\endgroup\$G. Sliepen– G. Sliepen2025年05月11日 19:11:28 +00:00Commented May 11 at 19:11