{"action":"created","issue":{"url":"https://api.github.com/repos/boostorg/beast/issues/1381","repository_url":"https://api.github.com/repos/boostorg/beast","labels_url":"https://api.github.com/repos/boostorg/beast/issues/1381/labels{/name}","comments_url":"https://api.github.com/repos/boostorg/beast/issues/1381/comments","events_url":"https://api.github.com/repos/boostorg/beast/issues/1381/events","html_url":"https://github.com/boostorg/beast/issues/1381","id":393095623,"node_id":"MDU6SXNzdWUzOTMwOTU2MjM=","number":1381,"title":"Multi-threaded server example for synchronization","user":{"login":"bandzaw","id":24864195,"node_id":"MDQ6VXNlcjI0ODY0MTk1","avatar_url":"https://avatars1.githubusercontent.com/u/24864195?v=4","gravatar_id":"","url":"https://api.github.com/users/bandzaw","html_url":"https://github.com/bandzaw","followers_url":"https://api.github.com/users/bandzaw/followers","following_url":"https://api.github.com/users/bandzaw/following{/other_user}","gists_url":"https://api.github.com/users/bandzaw/gists{/gist_id}","starred_url":"https://api.github.com/users/bandzaw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bandzaw/subscriptions","organizations_url":"https://api.github.com/users/bandzaw/orgs","repos_url":"https://api.github.com/users/bandzaw/repos","events_url":"https://api.github.com/users/bandzaw/events{/privacy}","received_events_url":"https://api.github.com/users/bandzaw/received_events","type":"User","site_admin":false},"labels":[{"id":681119875,"node_id":"MDU6TGFiZWw2ODExMTk4NzU=","url":"https://api.github.com/repos/boostorg/beast/labels/Example","name":"Example","color":"0e8a16","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":9,"created_at":"2018-12-20T15:31:08Z","updated_at":"2018-12-21T15:43:25Z","closed_at":null,"author_association":"NONE","body":"I use Boost.Beast WebSockets version 1.68 for a multihreaded application that I'm currently developing.\r\n\r\nUnfortunately I just got the \"Assertion `id_ != T::id' failed\" from the try_lock function in boost_1_68_0/boost/beast/websocket/detail/stream_base.hpp:105 under heavy load.\r\n\r\nTo my surprise this is how it obviously is supposed to work; the comment in the source code of try_lock function clearly says that this will happen if I call an async function before the previous call to the same async function has *completed*.\r\n\r\nI therefore thought that a quick fix would be to guard the call to the async function with a mutex:\r\n\r\n```\r\nvoid WSSession::send(std::shared_ptr<uint8_t[]> data, size_t length)  \r\n{\r\n    std::lock_guard<std::mutex> lck(write_mutex_);\r\n    ws_.async_write(  \r\n        boost::asio::buffer(data.get(), length),\r\n        boost::asio::bind_executor(\r\n            strand_,\r\n            [data](boost::system::error_code ec, std::size_t bytes_transferred)\r\n            {\r\n                ...\r\n            }\r\n        )\r\n    );\r\n}\r\n```\r\nBut then I get the same problem still. Hmm, does _completed_ in the code's comment really mean that the handler must have been called before I'm allowed to call the async function again!? Let's try:\r\n```\r\nvoid WSSession::send(std::shared_ptr<uint8_t[]> data, size_t length)  \r\n{\r\n    write_mutex_.lock();\r\n    ws_.async_write(  \r\n        boost::asio::buffer(data.get(), length),\r\n        boost::asio::bind_executor(\r\n            strand_,\r\n            [&write_mutex_,data](boost::system::error_code ec, std::size_t bytes_transferred)\r\n            {\r\n                ...\r\n                write_mutex_.unlock();\r\n            }\r\n        )\r\n    );\r\n}\r\n```\r\nNow this works - yay!\r\n\r\nBut this solution feels like overkill, I could as well use the synchronous API if I have to add a mutex and handle its locking and unlocking manually. Also, I have not seen this convoluted construction before, i.e. locking a mutex in a async function and unlocking it in its handler, which clearly is a sign that I'm doing something stupid...\r\n\r\nBut... I thought that all of this would be automagically handled by the strand that I'm using, that's the whole purpose of it! The documentation of strand states: \r\n\r\n> A strand is defined as a strictly sequential invocation of event handlers (i.e. no concurrent invocation). Use of strands allows execution of code in a multithreaded program without the need for explicit locking (e.g. using mutexes).\r\n\r\nSo my question is, do I really need both belt and suspenders, or is this maybe a bug in the strand implementation or perhaps in its documentation, because clearly the strand does not allow for the execution of code in my multithreaded program without the need for explicit locking using mutexes!? However, I think it is more likely that I have missed something fundamental - but what?\r\n"},"comment":{"url":"https://api.github.com/repos/boostorg/beast/issues/comments/449421835","html_url":"https://github.com/boostorg/beast/issues/1381#issuecomment-449421835","issue_url":"https://api.github.com/repos/boostorg/beast/issues/1381","id":449421835,"node_id":"MDEyOklzc3VlQ29tbWVudDQ0OTQyMTgzNQ==","user":{"login":"vinniefalco","id":1503976,"node_id":"MDQ6VXNlcjE1MDM5NzY=","avatar_url":"https://avatars3.githubusercontent.com/u/1503976?v=4","gravatar_id":"","url":"https://api.github.com/users/vinniefalco","html_url":"https://github.com/vinniefalco","followers_url":"https://api.github.com/users/vinniefalco/followers","following_url":"https://api.github.com/users/vinniefalco/following{/other_user}","gists_url":"https://api.github.com/users/vinniefalco/gists{/gist_id}","starred_url":"https://api.github.com/users/vinniefalco/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vinniefalco/subscriptions","organizations_url":"https://api.github.com/users/vinniefalco/orgs","repos_url":"https://api.github.com/users/vinniefalco/repos","events_url":"https://api.github.com/users/vinniefalco/events{/privacy}","received_events_url":"https://api.github.com/users/vinniefalco/received_events","type":"User","site_admin":false},"created_at":"2018-12-21T15:43:25Z","updated_at":"2018-12-21T15:43:25Z","author_association":"MEMBER","body":"> ...that I really needed to do all this was not obvious at all to me when I started to develop my app.\r\n\r\nBased on my experiences and observations, it really does look like we are missing crucial documentation and tutorials on asynchronous networking concepts and idioms. The Asio docs are okay but they could be a lot better. That said, I don't think we should put the burden of this all on the Asio author. We need to take collective responsibility for it and produce this literature ourselves. I've done some of that in Beast but clearly I need to go farther. I'm working on it :)\r\n"}}
{"actor":{"display_login":"vinniefalco"}}