{"action":"created","issue":{"url":"https://api.github.com/repos/celluloid/celluloid-io/issues/132","labels_url":"https://api.github.com/repos/celluloid/celluloid-io/issues/132/labels{/name}","comments_url":"https://api.github.com/repos/celluloid/celluloid-io/issues/132/comments","events_url":"https://api.github.com/repos/celluloid/celluloid-io/issues/132/events","html_url":"https://github.com/celluloid/celluloid-io/issues/132","id":57497169,"number":132,"title":"Actor crash when reads and writes both block","user":{"login":"phopkins","id":260837,"avatar_url":"https://avatars.githubusercontent.com/u/260837?v=3","gravatar_id":"","url":"https://api.github.com/users/phopkins","html_url":"https://github.com/phopkins","followers_url":"https://api.github.com/users/phopkins/followers","following_url":"https://api.github.com/users/phopkins/following{/other_user}","gists_url":"https://api.github.com/users/phopkins/gists{/gist_id}","starred_url":"https://api.github.com/users/phopkins/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phopkins/subscriptions","organizations_url":"https://api.github.com/users/phopkins/orgs","repos_url":"https://api.github.com/users/phopkins/repos","events_url":"https://api.github.com/users/phopkins/events{/privacy}","received_events_url":"https://api.github.com/users/phopkins/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-02-12T18:08:58Z","updated_at":"2015-02-12T18:16:24Z","closed_at":null,"body":"Hi folks! I'm coming to Celluloid IO via the Krakow NSQ client gem. I have a reproduction of a bug that we've been seeing there, but I'm not sure if it's a bug in Celluloid IO or a bug in Krakow's assumptions about Celluloid IO.\r\n\r\nThe error we're seeing is “ArgumentError: this IO is already registered with selector” come from the `monitor = @selector.register(io, set)` line in reactor.rb’s `wait` method.\r\n\r\nThe code opens a TCP socket and has a permanent read loop, run via `async`. It then receives writes (usually synchronously). The bug seems to occur when, during write, an `:IO::WaitWritable` exception is thrown (see stream.rb's `syswrite` method). This causes Celluloid IO to `wait_writeable` on the actor, leading to a call to reactor.rb's `wait` method.\r\n\r\nAt this point, the reactor's selector is already registered on the `io` due to the `recv` in the read loop. When it registers again for the blocked write, the selector implementation raises an exception due to the double-registration of the same IO object.\r\n\r\nHere are two files that demonstrate the problem. It takes maybe 10 iterations on my machine to see the exception.\r\n\r\n```\r\n# server.rb\r\nrequire 'socket'\r\n\r\nserver = TCPServer.new 2000 # Server bind to port 2000\r\n\r\nloop do\r\n  server.accept\r\nend\r\n```\r\n\r\n```\r\n# client.rb\r\nrequire 'celluloid/io'\r\n\r\nclass Client\r\n  include Celluloid::IO\r\n\r\n  def init!\r\n    @socket = Celluloid::IO::TCPSocket.new(\"localhost\", 2000)\r\n    async.read_loop\r\n  end\r\n\r\n  def read_loop\r\n    while true\r\n      puts \"READING\"\r\n      @socket.recv(8)\r\n    end\r\n  end\r\n\r\n  def write\r\n    @socket.write (0...100000).map { ('a'..'z').to_a[rand(26)] }.join\r\n  end\r\nend\r\n\r\nclient = Client.new()\r\nclient.init!\r\n\r\nloop do\r\n  puts \"WRITING\"\r\n  client.write\r\nend\r\n```\r\n\r\nThe error trace on my box is:\r\n```\r\n$ ~/c/celluloid-test [1]> ruby ./client.rb\r\nREADING\r\nWRITING\r\nWRITING\r\nWRITING\r\nWRITING\r\nWRITING\r\nWRITING\r\nWRITING\r\nD, [2015-02-12T13:05:01.978070 #62944] DEBUG -- : Terminating 1 actor...\r\nE, [2015-02-12T13:05:01.978205 #62944] ERROR -- : Actor crashed!\r\nArgumentError: this IO is already registered with selector\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io/reactor.rb:42:in `register'\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io/reactor.rb:42:in `wait'\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io/reactor.rb:26:in `wait_writable'\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io.rb:65:in `wait_writable'\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io/stream.rb:33:in `wait_writable'\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io/stream.rb:63:in `rescue in block in syswrite'\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io/stream.rb:60:in `block in syswrite'\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io/stream.rb:386:in `synchronize'\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io/stream.rb:58:in `syswrite'\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io/stream.rb:352:in `do_write'\r\n\t/Users/phopkins/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.1/lib/celluloid/io/stream.rb:246:in `write'\r\n\t./client.rb:20:in `write'\r\n```\r\n\r\n\r\n\r\nIs this considered a bug in Celluloid IO, or is this simultaneous read/write on the same TCPSocket that Krakow is doing not a good pattern?\r\n\r\nThanks for any help on this issue. I'm happy to give a fix a try if someone can sketch out what they think would be good; I'm wondering about having both a “read” and a “write” selector inside the reactor for the waits (since latches elsewhere in the code ensure that those are exclusive) but there are some details there that don’t quite fit."},"comment":{"url":"https://api.github.com/repos/celluloid/celluloid-io/issues/comments/74122154","html_url":"https://github.com/celluloid/celluloid-io/issues/132#issuecomment-74122154","issue_url":"https://api.github.com/repos/celluloid/celluloid-io/issues/132","id":74122154,"user":{"login":"phopkins","id":260837,"avatar_url":"https://avatars.githubusercontent.com/u/260837?v=3","gravatar_id":"","url":"https://api.github.com/users/phopkins","html_url":"https://github.com/phopkins","followers_url":"https://api.github.com/users/phopkins/followers","following_url":"https://api.github.com/users/phopkins/following{/other_user}","gists_url":"https://api.github.com/users/phopkins/gists{/gist_id}","starred_url":"https://api.github.com/users/phopkins/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/phopkins/subscriptions","organizations_url":"https://api.github.com/users/phopkins/orgs","repos_url":"https://api.github.com/users/phopkins/repos","events_url":"https://api.github.com/users/phopkins/events{/privacy}","received_events_url":"https://api.github.com/users/phopkins/received_events","type":"User","site_admin":false},"created_at":"2015-02-12T18:16:24Z","updated_at":"2015-02-12T18:16:24Z","body":"I should add that my expected behavior for my repro script is for the writes to eventually block (since the server isn't consuming any of the data).\r\n\r\nThis is coming up in production because of network latency or some other reason causing the `syswrite` call to occasionally receive an `:IO::WaitWritable` exception."}}