-
-
Save rparker/978836 to your computer and use it in GitHub Desktop.
------------------------------------------------------------------------ | |
this 12-line file is "send.rb" | |
------------------------------------------------------------------------ | |
#!/usr/bin/env ruby | |
require 'rubygems' | |
require 'socket' | |
sock = TCPSocket.new 'localhost', 9814 | |
t0 = Time.now | |
sock.send 'start working', 0 | |
reply, sender_addrinfo = sock.recvfrom(16) | |
printf("%8.4fs later, got the stale reply: %s\n", (Time.now - t0), reply) | |
------------------------------------------------------------------------ | |
and this file is 'clairvoyant.rb' | |
------------------------------------------------------------------------ | |
#!/usr/bin/env ruby | |
require 'rubygems' | |
require 'eventmachine' | |
class Feeder < EventMachine::Connection⋅ | |
def initialize(q)⋅ | |
@q = q⋅ | |
end⋅ | |
def receive_data(cmd)⋅ | |
send_data cmd + '.'*(16 - cmd.size) | |
puts 'Reply has been sent, entangled with the work which has yet to begin' | |
@q.push cmd⋅ | |
end⋅ | |
end⋅ | |
# - - - - - - - - - - - - -⋅ | |
class Worker | |
# This busy loop takes >50ms on my mac. | |
# If you comment out the work, the reply is received in < 1ms. | |
def busy(str) | |
puts 'The future work period has yet to begin...' | |
10.times{system 'ls'} | |
puts 'work completed' | |
end | |
end | |
# - - - - - - - - - - - - -⋅ | |
class Clairvoyant | |
def initialize | |
puts 'Clair is listening' | |
end | |
def run | |
worker = Worker.new | |
EventMachine::run {⋅ | |
q = EM::Channel.new⋅ | |
EM::start_server '127.0.0.1', 9814, Feeder, q⋅ | |
unused = q.subscribe{|msg| worker.busy(msg)}⋅ | |
}⋅ | |
end | |
end | |
# - - - - - - - - - - - - -⋅ | |
clair = Clairvoyant.new | |
clair.run |
Thanks - you're right. But the actual program actually does some work instead of sleeping. I just put 'sleep 2' in the isolated example for simplicity, and it was a very misleading simplification. The EM timers aren't a one-line replacement for sleep, so I've changed to: 10.times{system 'ls'} Which takes more than 50ms on my system. With no "work" being done, the reply is received in less than one millisecond, so the problem still remains.
Somehow, the socket data is not being received in the 'send.rb' program until AFTER the work gets done.
That's still going to block the reactor. It will execute all of the 10.times { system 'ls' } commands before the reactor loop runs again. The reactor loop has to turn in order for the messages to get sent out.
You either need to turn the blocking operations into non-blocking or use something like EM.defer to run them on a background thread.
The problem is you can't use sleep inside an EM program. You'll be putting the entire reactor to sleep and, this will probably happen before the message is sent.
Instead of sleep, can you use something along the lines of EM.add_timer(2) { puts 'wakeup' }?