c++websocketboost-asioboost-beast

Can you unblock boost::asio::io_context while waiting for async_read?


im trying to connect to a server via boost asio and beast. I need to send heartbeats to the server every 40 seconds, but when I try to, my write requests get stuck in a queue and never get executed, unless the server sends something first.

I have this code to look for new messages that come in.

this->ioContext.run();

thread heartbeatThread(&client::heartbeatCycle, this);

while (this->p->is_socket_open()) {
    this->ioContext.restart();
    this->p->asyncQueue("", true);
    this->ioContext.run();
}

The asyncQueue function just calls async_read, and blocks the io context. The heartbeatCycle tries to send heartbeats, but gets stuck in the queue. If I force it to send anyways, I get

Assertion failed: (id_ != T::id), function try_lock, file soft_mutex.hpp, line 89.

When the server sends a message, the queue is unblocked, and all the queued messages go through, until there is no more work, and the io_context starts blocking again.

So my main question is, is there any way to unblock the io context without having the server send a message? If not, is there a way to emulate the server sending a message?

Thanks!

EDIT:

I have this queue function that queues messages being sent called asyncQueue.

void session::asyncQueue(const string& payload, const bool& madeAfterLoop)
{
    if(!payload.empty())
    {
        queue_.emplace_back(payload);
    }

    if(payload.empty() && madeAfterLoop)
    {
        queue_.emplace_back("KEEPALIVE");
    }

    // If there is something to write, write it.
    if(!currentlyQueued_ && !queue_.empty() && queue_.at(0) != "KEEPALIVE")
    {
        currentlyQueued_ = true;
        ws_.async_write(
                net::buffer(queue_.at(0)),
                beast::bind_front_handler(
                        &session::on_write,
                        shared_from_this()));
        queue_.erase(queue_.begin());
    }

    // If there is nothing to write, read the buffer to keep stream alive
    if(!currentlyQueued_ && !queue_.empty())
    {
        currentlyQueued_ = true;
        ws_.async_read(
                buffer_,
                beast::bind_front_handler(
                        &session::on_read,
                        shared_from_this()));
        queue_.erase(queue_.begin());
    }
}

The problem is when the code has nothing no work left to do, it calls async read, and gets stuck until the server sends something.

In the function where I initialized the io_context, I also created a separate thread to send heartbeats every x seconds.

void client::heartbeatCycle()
{
    while(this->p->is_socket_open())
    {
        this->p->asyncQueue(bot::websocket::sendEvents::getHeartbeatEvent(cache_), true );
        this_thread::sleep_for(chrono::milliseconds(10000));
    }
}

Lastly, I have these 2 lines in my on_read function that runs whenever async read is called.

currentlyQueued_ = false;
asyncQueue();

Once there is no more work to do, the program calls async_read but currentlyQueued_ is never set to false.

The problem is the io_context is stuck looking for something to read. What can I do to stop the io_context from blocking the heartbeats from sending?

The only thing I have found that stops the io_context from blocking is when the server sends me a message. When it does, currentlyQueued_ is set to false, and the queue able to run and the queue is cleared.

That is the reason im looking for something that can emulate the server sending me a message. So is there a function that can do that in asio/beast? Or am I going about this the wrong way. Thanks so much for your help.


Solution

  • The idea is to run the io_service elsewhere (on a thread, or in main, after starting an async chain).

    Right now you're calling restart() on it which simply doesn't afford continuous operation. Why stop() or let it run out of work at all?

    Note, manually starting threads is atypical and unsafe.

    I would give examples, but lots already exist (also on this site). I'd need to see question code with more detail to give concrete suggestions.