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, [handle]() mutable
        {
            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) -> boost::asio::awaitable<void>
{
    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. Аналогичным образом реализована поддержка короутин у таймеров и сокетов. Тест:

#include "schedule.h"

#include <boost/asio/io_context.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(test_schedule)
{
    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> {
            BOOST_TEST(reachedPointA);
            BOOST_TEST(!reachedPointB);
            co_return;
    };

    auto main = [&]() -> 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, main(), boost::asio::detached);
    ioContext.run();

    BOOST_TEST(reachedPointA);
    BOOST_TEST(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