multithreadingboost-asioepoll

Is it possible to change the io_context of a socket in boost::asio?


I'm currently writing a multi-threaded server where each thread has an io_context and a list of task objects to execute, with each task object having an associated ip::tcp::socket object.

For load-balancing I'm sometimes migrating tasks from one thread to another, however I'd like to migrate their sockets as well without dropping connection.

I could simply pass ownership of the socket object between threads, however the socket's io_context will remain that of its original thread, which would add significant complexity/slow-down.

Is there any way for me to keep a socket connection whilst moving it to a different io_context? Or is there another recommended approach?

Many thanks


Solution

  • You can not change the io_context directly, but there's a workaround

    just use release and assign

    here's an example:

    const char* buff = "send";
    boost::asio::io_context io;
    boost::asio::io_context io2;
    boost::asio::ip::tcp::socket socket(io);
    socket.open(boost::asio::ip::tcp::v4());
    
    std::thread([&](){
        auto worker1 = boost::asio::make_work_guard(io);
        io.run();
    }).detach();
    std::thread([&](){
        auto worker2 = boost::asio::make_work_guard(io2);
        io2.run();
    }).detach();
    
    socket.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::from_string("127.0.0.1"), 8888));
    
    socket.async_send(boost::asio::buffer(buff, 4),
            [](const boost::system::error_code &ec, std::size_t bytes_transferred)
            {
                std::cout << "send\n";
                fflush(stdout);
            });
    
    // any pending async ops will get boost::asio::error::operation_aborted
    auto fd = socket.release();
    // create another socket using different io_context
    boost::asio::ip::tcp::socket socket2(io2);
    // and assign the corresponding fd
    socket2.assign(boost::asio::ip::tcp::v4(), fd);
    // from now on io2 is the default executor of socket2
    socket2.async_send(boost::asio::buffer(buff, 4),
            [](const boost::system::error_code &ec, std::size_t bytes_transferred){
                std::cout << "send via io2\n";
                fflush(stdout);
            });
    
    getchar();