Boost.ASIO coroutines. Scheduler.

Для реализации примитивов синхронизации короутин (например sequence_barrier) нужен механизм для приостановки короутины до следующей итерации цикла событий. По сути это аналог функции pthread_yield для синхронного кода.

Пример использования выглядит как-то так:

do {
    co_await schedule(executor);
    do_some_thing();
} while(condition);

Типовая реализация:

struct Awaiter
{
    boost::asio::any_io_executor executor;

    bool await_ready() { return false; }
    void await_suspend(std::coroutine_handle<> handle) {
        boost::asio::post(executor, [executor, handle]()
        {
            handle.resume();
        });
    }
    void await_resume() {}
}

Awaiter schedule(boost::asio::any_io_executor executor) {
	return Awaiter{executor};
}

Но для короутин boost::asio::awaitable это не подходит. Поэтому сделаем реализацию средствами Boost, используя функцию boost::asio::async_initiate инстанционирующую специализацию класса boost::asio::async_result для use_awaitable.

inline auto schedule(boost::asio::any_io_executor executor)
{
    auto initiate = [executor]<typename Handler>(Handler&& handler) mutable
    {
        boost::asio::post(executor, [handler = std::forward<Handler>(handler)]() mutable
        {
            handler();
        });
    };

    return boost::asio::async_initiate<
            decltype(boost::asio::use_awaitable), void()>(
                initiate, boost::asio::use_awaitable);
}

В async_initiate передается функтор (выполняющий планирование пробуждения и вызова обработчика завершения) и экземпляр use_awaitable. Аналогичным образом реализована поддержка короутин у таймеров и сокетов. Тест:

TEST(schedule_asio, _) {
    bool reachedPointA = false;
    bool reachedPointB = false;

    auto process = [&]() -> boost::asio::awaitable<void> {
            reachedPointA = true;

            boost::asio::any_io_executor executor = co_await boost::asio::this_coro::executor;
            co_await schedule(executor);

            reachedPointB = true;
            co_return;
    };


    auto check = [&]() -> boost::asio::awaitable<void> {
            EXPECT_TRUE(reachedPointA);
            EXPECT_FALSE(reachedPointB);
            co_return;
    };


    auto task = [&]() -> boost::asio::awaitable<void> {
            using namespace boost::asio::experimental::awaitable_operators;
            co_await(process() && check());
            co_return;
    };

    boost::asio::io_context ioContext;
    boost::asio::co_spawn(ioContext, task(), boost::asio::detached);
    ioContext.run();

    EXPECT_TRUE(reachedPointA);
    EXPECT_TRUE(reachedPointB);
}

Subscribe to Заметочки

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe