rubymultithreadingem-websocket

Send multiply messages in websocket using threads


I'm making a Ruby server using the em-websocket gem. When a client sends some message (e.g. "thread") the server creates two different threads and sends two anwsers to the client in parallel (I'm actually studying multithreading and websockets). Here's my code:

EM.run {
  EM::WebSocket.run(:host => "0.0.0.0", :port => 8080) do |ws|
    ws.onmessage { |msg|
      puts "Recieved message: #{msg}"
      if msg == "thread"
        threads = []
        threads << a = Thread.new {
          sleep(1)
          puts "1"
          ws.send("Message sent from thread 1")
        }
        threads << b = Thread.new{
          sleep(2)
          puts "2"
          ws.send("Message sent from thread 2")
        }
        threads.each { |aThread| aThread.join }
      end

How it executes:

The problem is that I want to send messages exactly at the same time when debug output "1" and "2" are sent.

My Ruby version is 1.9.3p194.


Solution

  • I don't have experience with EM, so take this with a pinch of salt.

    However, at first glance, it looks like "aThread.join" is actually blocking the "onmessage" method from completing and thus also preventing the "ws.send" from being processed. Have you tried removing the "threads.each" block?

    Edit: After having tested this in arch linux with both ruby 1.9.3 and 2.0.0 (using "test.html" from the examples of em-websocket), I am sure that even if removing the "threads.each" block doesn't fix the problem for you, you will still have to remove it as Thread#join will suspend the current thread until the "joined" threads are finished.

    If you follow the function call of "ws.onmessage" through the source code, you will end up at the Connection#send_data method of the Eventmachine module and find the following within the comments:

    Call this method to send data to the remote end of the network connection. It takes a single String argument, which may contain binary data. Data is buffered to be sent at the end of this event loop tick (cycle).

    As "onmessage" is blocked by the "join" until both "send" methods have run, the event loop tick cannot finish until both sets of data are buffered and thus, all the data cannot be sent until this time.

    If it is still not working for you after removing the "threads.each" block, make sure that you have restarted your eventmachine and try setting the second sleep to 5 seconds instead. I don't know how long a typical event loop takes in eventmachine (and I can't imagine it to be as long as a second), however, the documentation basically says that if several "send" calls are made within the same tick, they will all be sent at the same time. So increasing the time difference will make sure that this is not happening.