beast: Boost 1.66 upgrade: assertion failure and mixing async/sync ops
I recently upgraded from Beast beta 0.69 to official Boost 1.66. (Belated congratz to Vinnie đ
I have a SSL websocket client test application for sending various test traffic to my SSL websocket server application. This test client can create multiple concurrent websocket connections and multiple worker threads all managed under a single asio::io_service. Each connection session uses a mix of asynchronous reads and synchronous writes (including synchronous websocket::stream::close()). The async read callbacks are wrapped by a strand. The synchronous writes/close are executed from within strand-wrapped callbacks or strand-posted functions. There is at most one active async_read() at a time.
This test client worked fine with Beast 0.69. After upgrading to Boost 1.66, I noticed that when these client websocket connections attempt to initiate the close:
- websocket::stream::close() always produced the error code websocket::error::failed (âboost.beast.websocket: WebSocket connection failed due to a protocol violationâ)
- Sometimes one or more of the connection sessions would assert fail at: beast/websocket/impl/read.ipp:576 Assertion `ws_.status_ == status::openâ failed.
As an experiment, I changed close() to async_close() and then I noticed:
- The error code websocket::error::failed went away. async_closeâs callback always receives a success status.
- The read.ipp:576 âws_.status_ == status::openâ assertion failure went away.
- A new assertion failure appeared, but much less frequently: beast/websocket/impl/read.ipp:217 Assertion `ws_.rd_block_â failed. The async_read() callback handler was not reached, so Iâm guessing this asserted while processing the peer close frame response from the server.
Questions:
- Is it incorrect to mix async and sync calls?
I understand that there can be no more than one active async read, write, ping, close op without an intervening corresponding callback. I also understand that websocket objects are not thread safe so some kind of synchronization control (like an explicit strand) is needed when using multiple threads.
I think Iâm following these rules⌠But perhaps if websocket::stream::close() is internally performing async ops on the websocket, then these async ops might get executed on another thread without strand protection. This could race with my earlier call to async_read() if the timing is unlucky and end up with two threads modifying the same websocket.
But the docs for close() says itâs implemented with the next layerâs write_some() which are blocking calls.
Not sure if this is my bug or Beast bug
- what might be causing the new assertion failure at read.ipp:217?
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 2
- Comments: 86 (41 by maintainers)
Commits related to this issue
- Add asio_handler_invoke overloads for stream algorithms: fix #1012 This fixes a bug where asynchronous stream alogrithms do not work correctly with the legacy `io_service::strand` implementation. — committed to vinniefalco/beast by vinniefalco 6 years ago
- Add asio_handler_invoke overloads for stream algorithms: fix #1012 This fixes a bug where asynchronous stream alogrithms do not work correctly with the legacy `io_service::strand` implementation. — committed to vinniefalco/beast by vinniefalco 6 years ago
- Add asio_handler_invoke overloads for stream algorithms: fix #1012 This fixes a bug where asynchronous stream alogrithms do not work correctly with the legacy `io_service::strand` implementation. — committed to vinniefalco/beast by vinniefalco 6 years ago
The Boost.Asio author has informed me that overloads of
asio_handler_invokeare still needed if composed operations are to work with the old-style strands (io_service::strand), which you are using. I am working on a fix which should resolve this issue, thanks for sticking through it. I expect that the non-SSL version will also malfunction.