4
\$\begingroup\$

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?

asked May 9 at 9:35
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

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_awaits a Delay, the Scheduler should switch to another task that has something to do, or sleep until one of the delays times out.

answered May 10 at 10:25
\$\endgroup\$
2
  • \$\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\$ Commented May 11 at 7:18
  • 1
    \$\begingroup\$ I'm not saying how you should implement Scheduler and Delay, I'm just giving an example of much more realistic code using a hypothetical scheduler than your main(). 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\$ Commented May 11 at 19:11

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.