Closed u3shit closed 2 years ago
If you want to write to a websocket from multiple tasks, you need to protect it with a Async::Semaphore
, otherwise the underlying socket might do a partial write and then mix up the data. Since there is a cost to having a semaphore, it's not the default at the lower level. There are lots of ways to deal with this - an Async::Queue
for buffering messages, or a semaphore for mutual exclusion, etc.
Okay, thanks, I've refactored my code to put messages into an Async::LimitedQueue
instead, hopefully that means less context switches that a semaphore. Right now I have two tasks, one reads messages in a loop and the other writes them (the chat example does something like this, so it should be alright).
I have a client that calls
write_message
from multiple async tasks, and it looks like sometimes the messages get mixed up. Here's a simple example that just sends the same byte many times and on the other end it verifies it:I actually have to either use
ruby --jit
with the client or use the workaround I mentioned in https://github.com/socketry/protocol-websocket/issues/8 otherwise it doesn't seem to manifest, but I that's probably just a timing issue. I end up with errors like"\xE4\xC1\xE1[f>\xE1Kf>\x0F`u\x03c\xE9\x9BS\x965\xE7/\xEAI\xE7/\xEAI\xE7/\xEAI\xE7/\xEAI\xE7/\xEAI\xE7/\xEAI\xE7/\xEAI\xE7/\xEAI\xE7/\xEAI\xE7/\xEAI\xE7/\xEAI\xE7/\xEAI\xE7/c\xA4f>\xE1[f.\xE1[\xDF\xA9\x1A\x05\x8C\xFAIV\x8C\xFAIV\x8C\xFAIV\x8C\xFAIV"
, or without masking"\x82\x7F\x00\x00\x00\x00\x00\x10\x00\x00\x82\x7F\x00\x00\x00\x00\x00\x10\x00\x00\x82\x7F\x00\x00\x00\x00\x00\x10\x00\x00\x82\x7F\x00\x00\x00\x00\x00\x10\x00\x00\x82\x7F\x00\x00\x00\x00\x00\x10\x00\x00"
which eeriely looks like a websocket frame header repeated many times. This only happens with big messages, if I only send 50-100 byte messages it doesn't seem to manifest (it fits into a single write syscall?). Is this a supported usage or should I wrap my writes into some kind of mutex?