beast icon indicating copy to clipboard operation
beast copied to clipboard

Issue on not getting "on write"

Open treyweaver opened this issue 3 years ago • 9 comments

I am using the latest version of beast 1.74.

I have an issue where I have a mutex when I do a .async_write and I clear this mutex when I get an on_write event. I use this to make sure I don't do two writes in a row without waiting for the on_write event.

But sometimes I do a write and I do not get a on_write event. The mutex gets set and everything stops. I have tried a writingMtx.try_lock_for(Ms(50)) to try the mutex for 50ms then continue anyway but then when I do another write I get a SIGABRT fault because I did two writes in a row without getting an on_write event.

So how do I get around this problem? Is there some way to tell websockets to ignore the last write?

    bool CXXXWebSocket::writeBuffer(vector_u8 v, bool isBinary) {
        using Ms = std::chrono::milliseconds;
        if (!writingMtx.try_lock_for(Ms(50))) {
                myLog->warn("Write Buffer Mutex was locked for too long.");
                boost::ignore_unused(0);
        }
        boost::asio::mutable_buffers_1 buf = net::buffer(v.data(), v.size());
        std::cout << "xmit buffer size: " << buf.size() << std::endl;
        ws_.binary(true);
        ws_.async_write(
                        net::buffer(v.data(), v.size()),
                        beast::bind_front_handler(
                            &CJpsWebSocket::on_write,
                            shared_from_this()));
        return true;
    }

Here is my onWrite event.

    void CXXXWebSocket::on_write(
        beast::error_code ec,
        std::size_t bytes_transferred)
    {
        std::cout << "********** on_write..." << std::endl;
        writingMtx.unlock();
        onWriteMutex.lock();
        boost::ignore_unused(bytes_transferred);

        if(ec) {
            onWriteMutex.unlock();
            return fail(ec, "write");
        }

        if (!readIsReady) {
            readIsReady = true;
            readReady();
        }
        onWriteMutex.unlock();
    }

I have the timeouts set up as such.

        // Turn off the timeout on the tcp_stream, because
        // the websocket stream has its own timeout system.
        beast::get_lowest_layer(ws_).expires_never();

        // Set suggested timeout settings for the websocket
        ws_.set_option(
            websocket::stream_base::timeout::suggested(
                beast::role_type::client));

        // Set a decorator to change the User-Agent of the handshake
        ws_.set_option(websocket::stream_base::decorator(
            [](websocket::request_type& req)
            {
                req.set(http::field::user_agent,
                    std::string(BOOST_BEAST_VERSION_STRING) +
                        " websocket-client-async-ssl");
            }));

treyweaver avatar Jun 11 '21 19:06 treyweaver

Using mutexes with async io is almost always an error. Have you had a look at asio strands?

https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/reference/strand.html

madmongo1 avatar Jun 11 '21 19:06 madmongo1

The problem is not that the mutexs are not working. The problem is that I am calling .async_write and I never get an on_write event. And if this happens there does not seem to be any way to recover; the next .async_write you make will give a SIGABRT and the program will crash.

treyweaver avatar Jun 11 '21 19:06 treyweaver

Is this your first asynchronous ASIO program?

Have a look at this https://www.boost.org/doc/libs/1_76_0/doc/html/boost_asio/overview/core/strands.html

You need a "write queue."

vinniefalco avatar Jun 12 '21 04:06 vinniefalco

Just to be clear about this, you must not initiate another async_write before the execution of the completion handler of the previous async_write.

madmongo1 avatar Jun 12 '21 10:06 madmongo1

Apparently I have not made my issue clear.

I have packets in a que; I will get an object out of the que and send it using the .async_write command, wait for the handler to get called (in my case it is the on_write function) then get the next object out of the que and repeat.

Once in a while I will do a .async_write and the handler will NEVER get called! In that case what can I do? Can I "reset" the websocket somehow and continue?

treyweaver avatar Jun 12 '21 15:06 treyweaver

Are you able to share a minimal working example that demonstrate the problem please?

this is not behaviour we have observed.

madmongo1 avatar Jun 12 '21 19:06 madmongo1

I changed the architecture so that the async_write was only coming from one thread and the issue went away. Still not sure what the underlying issue was, but it is working now. You can close this issue. Thanks.

treyweaver avatar Jun 16 '21 19:06 treyweaver

async_write is not thread safe.

vinniefalco avatar Jun 18 '21 00:06 vinniefalco

This issue has been open for a while with no activity, has it been resolved?

stale[bot] avatar Jan 09 '22 03:01 stale[bot]