celluloid / celluloid-io

UNMAINTAINED: See celluloid/celluloid#779 - Evented sockets for Celluloid actors
https://celluloid.io
MIT License
879 stars 93 forks source link

Having a task waiting on a Celluloid::Condition prevents any non-blocking socket reads #50

Open benlangfeld opened 11 years ago

benlangfeld commented 11 years ago
require 'celluloid/io'

class Server
  include Celluloid::IO

  def initialize(host, port)
    @server = TCPServer.new host, port
    async.run
  end

  def run
    loop { async.handle_connection @server.accept }
  end

  def handle_connection(socket)
    loop { receive_data socket.readpartial(4096), socket }
  end

  def receive_data(data, client)
    Logger.debug "ServerMock receiving data: #{data}"
    client.write data
  end
end

class Client
  include Celluloid::IO

  def initialize(host, port)
    @host, @port = host, port
    Logger.debug "Starting up..."
    @condition = Celluloid::Condition.new
  end

  def run
    @socket = TCPSocket.new(@host, @port)
    loop { receive_data @socket.readpartial(4096) }
  end

  def send_data(data)
    @socket.write data

    @condition.wait if ARGV[0]
  end

  def bread
    @socket.to_io.readpartial(4096)
  end

  def read
    @socket.readpartial(4096)
  end

  def receive_data(data)
    Logger.debug "[RECV] #{data}"
    @condition.signal data
  end
end

server = Server.new '127.0.0.1', 5038

client = Client.new '127.0.0.1', 5038

client.async.run

sleep 1 # Wait for the socket to connect

fut1 = client.future.send_data 'password'
puts "Trying a blocking read..."
puts "Read #{client.bread.inspect}"

fut2 = client.future.send_data 'password'
puts "Trying a non-blocking read..."
puts "Read #{client.read.inspect}"

puts "Waiting on future values..."
puts "First: #{fut1.value}"
puts "Second: #{fut2.value}"

Without waiting...

1 ↵ ➭ bundle exec ruby mintest.rb                                                                                                                           [2.0.0]
D, [2013-04-09T19:01:02.371825 #60242] DEBUG -- : Starting up...
Trying a blocking read...
D, [2013-04-09T19:01:02.373009 #60242] DEBUG -- : ServerMock receiving data: password
Read "password"
Trying a non-blocking read...
D, [2013-04-09T19:01:02.373652 #60242] DEBUG -- : ServerMock receiving data: password
E, [2013-04-09T19:01:02.373996 #60242] ERROR -- : Client crashed!
Celluloid::ConditionError: can't wait unless owner
    /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/condition.rb:21:in `block in wait'
    /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/condition.rb:18:in `synchronize'
    /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/condition.rb:18:in `wait'
    /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-io/Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/condition.rb:21:in `block in wait': can't wait unless owner (Celluloid::ConditionError)
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/condition.rb:18:in `synchronize'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/condition.rb:18:in `wait'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-io-0.13.1/lib/celluloid/io/stream.rb:392:in `synchronize'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-io-0.13.1/lib/celluloid/io/stream.rb:48:in `sysread'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-io-0.13.1/lib/celluloid/io/stream.rb:134:in `readpartial'
    from mintest.rb:50:in `read'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/calls.rb:11:in `public_send'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/calls.rb:11:in `dispatch'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/calls.rb:63:in `dispatch'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/actor.rb:326:in `block in handle_message'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/tasks/task_fiber.rb:28:in `block in initialize'
    from (celluloid):0:in `remote procedure call'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/actor.rb:69:in `call'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/legacy.rb:14:in `method_missing'
    from mintest.rb:71:in `<main>'

With waiting

{19:01}~/code/ruby_ami:develop ✗
1 ↵ ➭ bundle exec ruby mintest.rb true                                                                                                                      [2.0.0]
D, [2013-04-09T19:01:08.983555 #60267] DEBUG -- : Starting up...
Trying a blocking read...
D, [2013-04-09T19:01:08.984872 #60267] DEBUG -- : ServerMock receiving data: password
Read "password"
Trying a non-blocking read...
/Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/condition.rb:21:in `block in wait': can't wait unless owner (Celluloid::ConditionError)
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/condition.rb:18:in `synchronize'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/condition.rb:18:in `wait'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-io-0.13.1/lib/celluloid/io/stream.rb:392:in `synchronize'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-io-0.13.1/lib/celluloid/io/stream.rb:48:in `sysread'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-io-0.13.1/lib/celluloid/io/stream.rb:134:in `readpartial'
    from mintest.rb:50:in `read'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/calls.rb:11:in `public_send'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/calls.rb:11:in `dispatch'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/calls.rb:63:in `dispatch'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/actor.rb:326:in `block in handle_message'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/tasks/task_fiber.rb:28:in `block in initialize'
    from (celluloid):0:in `remote procedure call'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/actor.rb:69:in `call'
    from /Users/ben/code/ruby_ami/vendor/ruby/2.0.0/gems/celluloid-0.13.0/lib/celluloid/legacy.rb:14:in `method_missing'
    from mintest.rb:71:in `<main>'