'Boost Beast async_write() With Queue

I have the following code:

std::queue< nlohmann::json > outgoingMessages;

void session::do_write( void ) {
    if ( outgoingMessages.size() > 0 ) {
        auto message = outgoingMessages.front();
        outgoingMessages.pop();

        ws_.async_write( boost::asio::buffer( message.dump() ), boost::beast::bind_front_handler( & session::on_write, shared_from_this() ) );
    }
};

void session::on_write( boost::beast::error_code errorCode, std::size_t bytes_transferred ) {
    if ( errorCode )
        return fail( errorCode, "write" );

    if ( bytes_transferred == 0 )
        std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) );

    do_write();
};

What I need it to do is only write when there is data to write. The problem is, when the queue is empty, async_write() never gets called, breaking the loop. Works great until it runs out of queue items to send.



Solution 1:[1]

I had the same issue and fixed it by recursively calling do_write() and check wether or not the queue contains new items. As long as you are making sure that async_write() is not called concurrently, you should be fine.

Try changing your code to:

std::queue< nlohmann::json > outgoingMessages;
bool isWriting = false;

void session::do_write( void ) {
    if ( !isWriting && outgoingMessages.size() > 0 ) {
        isWriting = true;
        auto message = outgoingMessages.front();
        
        ws_.async_write( boost::asio::buffer( outgoingMessages.dump() ), boost::beast::bind_front_handler( & session::on_write, shared_from_this() ) );
    } else
    {
        // Repeat write
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        do_write();
};

void session::on_write( boost::beast::error_code errorCode, std::size_t bytes_transferred ) {
    if ( errorCode )
        return fail( errorCode, "write" );

    if ( bytes_transferred == 0 )
        std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) );

    outgoingMessages.pop();

    // Repeat write for additional messages
    if (!outgoingMessages.empty())
    {
        ws_.async_write(
            boost::asio::buffer(outgoingMessages.front()),
            beast::bind_front_handler(
                &session::on_write,
                shared_from_this()));
    }
    else
    {
        isWriting = false;
        do_write();
    }
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 oerol