简体   繁体   中英

Use of Boost.Coroutine with Boost.ASIO causes assertion

I have boost::asio::io_context which is running in several threads (via ctx.run()) and I have several asynchronous objects that are used inside boost.coroutines, which are running via boost::asio::spawn. And I have the following assertion:

Assertion failed: ! is_running(), file C:\boost\boost_1_68_0\msvc_x86\include\boost-1_68\boost\coroutine\detail\push_coroutine_impl.hpp, line 258

I'm providing a minimal example which causes the same error. Please, help me: what am I doing wrong?

Boost version is 1.68

[Update]: there is a fix in the code (forget to lock io_context), but it doesn't affect assertion anyway.

#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>

template<class CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::system::error_code))
asyncDo(boost::asio::io_context& ctx, CompletionToken&& token)
{
    using CompletionType = boost::asio::async_completion<CompletionToken, void(boost::system::error_code)>;
    CompletionType completion{ token };
    ctx.post(
        [handler{ completion.completion_handler }](){
            using boost::asio::asio_handler_invoke;
            asio_handler_invoke(std::bind(handler, boost::system::error_code()), &handler);
            return;
        });
    return completion.result.get();
}

void coroFunc(boost::asio::io_context& ctx, boost::asio::yield_context yield)
{
    for (;;) {
        std::cerr << std::this_thread::get_id() << '\n';
        asyncDo(ctx, yield);
        std::cerr << std::this_thread::get_id() << '\n';
    }
}

int main(int, char* [])
{
    boost::asio::io_context ctx;
    boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work(ctx.get_executor());

    boost::asio::spawn(ctx, std::bind(coroFunc, std::ref(ctx), std::placeholders::_1));

    std::thread([&ctx]() { ctx.run(); }).detach();
    std::thread([&ctx]() { ctx.run(); }).detach();

    std::cin.get();
    work.reset();

    return 0;
}

After several hours of googling and trying I've got the solution that works for me (at least at my tests). The main idea is to replace posting to ctx with posting to associated executor :

    auto executor = boost::asio::get_associated_executor(completion.completion_handler, ctx);
    auto allocator = boost::asio::get_associated_allocator(completion.completion_handler);

    executor.post(
        [handler = HandlerType{ std::move(completion.completion_handler) }](){
            using boost::asio::asio_handler_invoke;
            asio_handler_invoke(std::bind(handler, boost::system::error_code()), &handler);
            return;
        },
        allocator);

Whole code:

#include <iostream>
#include <stdexcept>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>


template<class CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::system::error_code))
asyncDo(boost::asio::io_context& ctx, CompletionToken&& token)
{
    using CompletionType = boost::asio::async_completion<CompletionToken, void(boost::system::error_code)>;
    using HandlerType = typename CompletionType::completion_handler_type;
    CompletionType completion{ token };

    auto executor = boost::asio::get_associated_executor(completion.completion_handler, ctx);
    auto allocator = boost::asio::get_associated_allocator(completion.completion_handler);

    executor.post(
        [handler = HandlerType{ std::move(completion.completion_handler) }](){
            using boost::asio::asio_handler_invoke;
            asio_handler_invoke(std::bind(handler, boost::system::error_code()), &handler);
            return;
        },
        allocator);

    return completion.result.get();
}

void coroFunc(boost::asio::io_context& ctx, boost::asio::yield_context yield)
{
    for (;;) {
        try {
            std::cerr << "(0): " << std::this_thread::get_id() << '\n';
            asyncDo(ctx, yield);
            std::cerr << "(1): " << std::this_thread::get_id() << '\n';
        } catch (std::exception const& e) {
            std::cerr << "e: " << e.what() << '\n';
        }
    }
}

int main(int, char* [])
{
    boost::asio::io_context ctx;
    boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work(ctx.get_executor());

    boost::asio::spawn(ctx, std::bind(coroFunc, std::ref(ctx), std::placeholders::_1));

    std::thread([&ctx]() {
        for (;;) {
            try {
                ctx.run();
                break;
            } catch (std::exception const& e) {
                std::cerr << "e: " << e.what() << '\n';
            }
        }
    }).detach();
    std::thread([&ctx]() {
        for (;;) {
            try {
                ctx.run();
                break;
            } catch (std::exception const& e) {
                std::cerr << "e: " << e.what() << '\n';
            }
        }
    }).detach();

    std::cin.get();
    work.reset();

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM