Giter VIP home page Giter VIP logo

celluloid-io's Introduction

Celluloid

Gem Version MIT licensed Build Status Maintained: no Gitter Chat

Celluloid is a framework for building asynchronous and multithreaded Ruby programs using object-oriented concepts.

Revival Process Underway

Celluloid is in the process of being refactored and released back into the wild during Google Summer of Code. The next era will not have one individual active maintainer, but a team of collaborators. Going forward, previously dormant maintainer Donovan Keme is returning to support future primary maintainer Emese Padányi during GSoC 2020. Her plan extends past the Summer program, and aims to revive the community and codebase of Celluloid together. Backing this process are Harsh Deep and GSoC alumni Dilum Navanjana. We welcome your collaboration and contributions in this massive work.

The codebase is being refactored to pursue a stable release with no deprecation warnings, and with this cleaned up:

Diagram

Diagram meticulously developed by Emese Padányi

Proudly supported by the best cloud infrastructure provider in the world: DigitalOcean

Discussion

Documentation

Please see the Celluloid Wiki for more detailed documentation and usage notes.

The following API documentation is also available:

Related Projects

See also: Projects Using Celluloid

  • Reel: An "evented" web server based on Celluloid::IO
  • DCell: The Celluloid actor protocol distributed over 0MQ
  • ECell: Mesh strategies for Celluloid actors distributed over 0MQ
  • Celluloid::IO: "Evented" IO support for Celluloid actors
  • Celluloid::ZMQ: "Evented" 0MQ support for Celluloid actors
  • Celluloid::DNS: An "evented" DNS server based on Celluloid::IO
  • Celluloid::SMTP: An "evented" SMTP server based on Celluloid::IO
  • nio4r: "New IO for Ruby": high performance IO selectors
  • Timers: A generic Ruby timer library for event-based systems

Contributing to Celluloid

  • Fork this repository on github
  • Make your changes and send us a pull request
  • Pull requests will be reviewed for inclusion in the project

License

Copyright (c) 2011-2018 Tony Arcieri, Donovan Keme.

Distributed under the MIT License. See LICENSE.txt for further details.

celluloid-io's People

Contributors

asmod4n avatar benlangfeld avatar benlovell avatar bkerley avatar cesare avatar chewie avatar codekitchen avatar derekparker avatar dicom avatar digitalextremist avatar e1senh0rn avatar fbernier avatar halorgium avatar ioquatix avatar justinmcp avatar jwkoelewijn avatar kostya avatar kyledrake avatar laserlemon avatar nning avatar nono avatar paddor avatar rimenes avatar rjattrill avatar stopiccot avatar tarcieri avatar tbuehlmann avatar tonkpils avatar tricknotes avatar wteuber avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

celluloid-io's Issues

Java::JavaNioChannels::CancelledKeyException

While making another take on the issue #44
I've made a modification in gist https://gist.github.com/romansergey/5103388 :
Instead of initializing a pool, I've created a single actor:
@actor_pool = TestActor.new
and also bombed the server with ab
ab -c 1000 -n 5000 -k http://localhost:3000/

In most cases it crashes before serving the first 1k of requests with the following exception:

E, [2013-03-21T20:47:13.052000 #20440] ERROR -- : MyServer crashed!
Java::JavaNioChannels::CancelledKeyException: 
    sun.nio.ch.SelectionKeyImpl.ensureValid(SelectionKeyImpl.java:73)
    sun.nio.ch.SelectionKeyImpl.readyOps(SelectionKeyImpl.java:87)
    org.nio4r.Nio4r$Selector.processKey(Nio4r.java:300)
    org.nio4r.Nio4r$Selector.select(Nio4r.java:248)
    org.nio4r.Nio4r$Selector$INVOKER$i$select.call(Nio4r$Selector$INVOKER$i$select.gen)
    org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:177)
    org.jruby.runtime.callsite.CachingCallSite.callIter(CachingCallSite.java:188)
    rubyjit.Celluloid::IO::Reactor$$run_once_EC1FDB5E62FA19632A31BC859A049995CBB7493E1623557144.__file__(/home/forker/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-c15a60b73bfd/lib/celluloid/io/reactor.rb:49)
    rubyjit.Celluloid::IO::Reactor$$run_once_EC1FDB5E62FA19632A31BC859A049995CBB7493E1623557144.__file__(/home/forker/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-c15a60b73bfd/lib/celluloid/io/reactor.rb)
    org.jruby.ast.executable.AbstractScript.__file__(AbstractScript.java:42)
    org.jruby.internal.runtime.methods.JittedMethod.call(JittedMethod.java:181)
    org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:168)
    rubyjit.Celluloid::IO::Mailbox$$receive_3620F6E26F15C2E33D05EDCA22ECF3DDA2CDB4381623557144.chained_0_rescue_1$RUBY$SYNTHETIC__file__(/home/forker/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-c15a60b73bfd/lib/celluloid/io/mailbox.rb:53)
    rubyjit.Celluloid::IO::Mailbox$$receive_3620F6E26F15C2E33D05EDCA22ECF3DDA2CDB4381623557144.__file__(/home/forker/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-c15a60b73bfd/lib/celluloid/io/mailbox.rb)
    rubyjit.Celluloid::IO::Mailbox$$receive_3620F6E26F15C2E33D05EDCA22ECF3DDA2CDB4381623557144.__file__(/home/forker/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-c15a60b73bfd/lib/celluloid/io/mailbox.rb)
    org.jruby.ast.executable.AbstractScript.__file__(AbstractScript.java:42)
    org.jruby.internal.runtime.methods.JittedMethod.call(JittedMethod.java:181)
    org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:168)
    org.jruby.ast.CallOneArgNode.interpret(CallOneArgNode.java:57)
    org.jruby.ast.LocalAsgnNode.interpret(LocalAsgnNode.java:123)
    org.jruby.ast.IfNode.interpret(IfNode.java:110)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.WhileNode.interpret(WhileNode.java:131)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.RescueNode.executeBody(RescueNode.java:224)
    org.jruby.ast.RescueNode.interpret(RescueNode.java:119)
    org.jruby.ast.BeginNode.interpret(BeginNode.java:83)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.BlockNode.interpret(BlockNode.java:71)
    org.jruby.ast.RescueNode.executeBody(RescueNode.java:224)
    org.jruby.ast.RescueNode.interpret(RescueNode.java:119)
    org.jruby.evaluator.ASTInterpreter.INTERPRET_METHOD(ASTInterpreter.java:75)
    org.jruby.internal.runtime.methods.InterpretedMethod.call(InterpretedMethod.java:139)
    org.jruby.internal.runtime.methods.DefaultMethod.call(DefaultMethod.java:172)
    org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:306)
    org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:136)
    org.jruby.ast.VCallNode.interpret(VCallNode.java:88)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.BlockNode.interpret(BlockNode.java:71)
    org.jruby.evaluator.ASTInterpreter.INTERPRET_BLOCK(ASTInterpreter.java:112)
    org.jruby.runtime.Interpreted19Block.evalBlockBody(Interpreted19Block.java:209)
    org.jruby.runtime.Interpreted19Block.yield(Interpreted19Block.java:160)
    org.jruby.runtime.Interpreted19Block.yieldSpecific(Interpreted19Block.java:133)
    org.jruby.runtime.Block.yieldSpecific(Block.java:99)
    org.jruby.ast.ZYieldNode.interpret(ZYieldNode.java:25)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.EnsureNode.interpret(EnsureNode.java:96)
    org.jruby.ast.BeginNode.interpret(BeginNode.java:83)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.evaluator.ASTInterpreter.INTERPRET_BLOCK(ASTInterpreter.java:112)
    org.jruby.runtime.Interpreted19Block.evalBlockBody(Interpreted19Block.java:209)
    org.jruby.runtime.Interpreted19Block.yield(Interpreted19Block.java:197)
    org.jruby.runtime.Interpreted19Block.call(Interpreted19Block.java:128)
    org.jruby.runtime.Block.call(Block.java:89)
    org.jruby.RubyProc.call(RubyProc.java:261)
    org.jruby.RubyProc.call19(RubyProc.java:249)
    org.jruby.RubyProc$INVOKER$i$0$0$call19.call(RubyProc$INVOKER$i$0$0$call19.gen)
    org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:200)
    org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:196)
    org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:306)
    org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:136)
    org.jruby.ast.CallNoArgNode.interpret(CallNoArgNode.java:64)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.RescueNode.executeBody(RescueNode.java:224)
    org.jruby.ast.RescueNode.interpret(RescueNode.java:119)
    org.jruby.ast.BeginNode.interpret(BeginNode.java:83)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.BlockNode.interpret(BlockNode.java:71)
    org.jruby.ast.WhileNode.interpret(WhileNode.java:131)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.evaluator.ASTInterpreter.INTERPRET_BLOCK(ASTInterpreter.java:112)
    org.jruby.runtime.Interpreted19Block.evalBlockBody(Interpreted19Block.java:209)
    org.jruby.runtime.Interpreted19Block.yield(Interpreted19Block.java:197)
    org.jruby.runtime.Interpreted19Block.call(Interpreted19Block.java:128)
    org.jruby.runtime.Block.call(Block.java:89)
    org.jruby.RubyProc.call(RubyProc.java:261)
    org.jruby.RubyProc.call(RubyProc.java:213)
    org.jruby.internal.runtime.RubyRunnable.run(RubyRunnable.java:98)
    java.lang.Thread.run(Thread.java:722)
E, [2013-03-21T20:47:14.404000 #20440] ERROR -- : thread crashed
Java::JavaNioChannels::CancelledKeyException: 
    sun.nio.ch.SelectionKeyImpl.ensureValid(SelectionKeyImpl.java:73)
    sun.nio.ch.SelectionKeyImpl.readyOps(SelectionKeyImpl.java:87)
    org.nio4r.Nio4r$Selector.processKey(Nio4r.java:300)
    org.nio4r.Nio4r$Selector.select(Nio4r.java:248)
    org.nio4r.Nio4r$Selector$INVOKER$i$select.call(Nio4r$Selector$INVOKER$i$select.gen)
    org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:177)
    org.jruby.runtime.callsite.CachingCallSite.callIter(CachingCallSite.java:188)
    rubyjit.Celluloid::IO::Reactor$$run_once_EC1FDB5E62FA19632A31BC859A049995CBB7493E1623557144.__file__(/home/forker/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-c15a60b73bfd/lib/celluloid/io/reactor.rb:49)
    rubyjit.Celluloid::IO::Reactor$$run_once_EC1FDB5E62FA19632A31BC859A049995CBB7493E1623557144.__file__(/home/forker/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-c15a60b73bfd/lib/celluloid/io/reactor.rb)
    org.jruby.ast.executable.AbstractScript.__file__(AbstractScript.java:42)
    org.jruby.internal.runtime.methods.JittedMethod.call(JittedMethod.java:181)
    org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:168)
    rubyjit.Celluloid::IO::Mailbox$$receive_3620F6E26F15C2E33D05EDCA22ECF3DDA2CDB4381623557144.chained_0_rescue_1$RUBY$SYNTHETIC__file__(/home/forker/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-c15a60b73bfd/lib/celluloid/io/mailbox.rb:53)
    rubyjit.Celluloid::IO::Mailbox$$receive_3620F6E26F15C2E33D05EDCA22ECF3DDA2CDB4381623557144.__file__(/home/forker/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-c15a60b73bfd/lib/celluloid/io/mailbox.rb)
    rubyjit.Celluloid::IO::Mailbox$$receive_3620F6E26F15C2E33D05EDCA22ECF3DDA2CDB4381623557144.__file__(/home/forker/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-c15a60b73bfd/lib/celluloid/io/mailbox.rb)
    org.jruby.ast.executable.AbstractScript.__file__(AbstractScript.java:42)
    org.jruby.internal.runtime.methods.JittedMethod.call(JittedMethod.java:181)
    org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:168)
    org.jruby.ast.CallOneArgNode.interpret(CallOneArgNode.java:57)
    org.jruby.ast.LocalAsgnNode.interpret(LocalAsgnNode.java:123)
    org.jruby.ast.IfNode.interpret(IfNode.java:110)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.WhileNode.interpret(WhileNode.java:131)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.RescueNode.executeBody(RescueNode.java:224)
    org.jruby.ast.RescueNode.interpret(RescueNode.java:119)
    org.jruby.ast.BeginNode.interpret(BeginNode.java:83)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.BlockNode.interpret(BlockNode.java:71)
    org.jruby.ast.RescueNode.executeBody(RescueNode.java:224)
    org.jruby.ast.RescueNode.interpret(RescueNode.java:119)
    org.jruby.evaluator.ASTInterpreter.INTERPRET_METHOD(ASTInterpreter.java:75)
    org.jruby.internal.runtime.methods.InterpretedMethod.call(InterpretedMethod.java:139)
    org.jruby.internal.runtime.methods.DefaultMethod.call(DefaultMethod.java:172)
    org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:306)
    org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:136)
    org.jruby.ast.VCallNode.interpret(VCallNode.java:88)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.BlockNode.interpret(BlockNode.java:71)
    org.jruby.evaluator.ASTInterpreter.INTERPRET_BLOCK(ASTInterpreter.java:112)
    org.jruby.runtime.Interpreted19Block.evalBlockBody(Interpreted19Block.java:209)
    org.jruby.runtime.Interpreted19Block.yield(Interpreted19Block.java:160)
    org.jruby.runtime.Interpreted19Block.yieldSpecific(Interpreted19Block.java:133)
    org.jruby.runtime.Block.yieldSpecific(Block.java:99)
    org.jruby.ast.ZYieldNode.interpret(ZYieldNode.java:25)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.EnsureNode.interpret(EnsureNode.java:96)
    org.jruby.ast.BeginNode.interpret(BeginNode.java:83)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.evaluator.ASTInterpreter.INTERPRET_BLOCK(ASTInterpreter.java:112)
    org.jruby.runtime.Interpreted19Block.evalBlockBody(Interpreted19Block.java:209)
    org.jruby.runtime.Interpreted19Block.yield(Interpreted19Block.java:197)
    org.jruby.runtime.Interpreted19Block.call(Interpreted19Block.java:128)
    org.jruby.runtime.Block.call(Block.java:89)
    org.jruby.RubyProc.call(RubyProc.java:261)
    org.jruby.RubyProc.call19(RubyProc.java:249)
    org.jruby.RubyProc$INVOKER$i$0$0$call19.call(RubyProc$INVOKER$i$0$0$call19.gen)
    org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:200)
    org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:196)
    org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:306)
    org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:136)
    org.jruby.ast.CallNoArgNode.interpret(CallNoArgNode.java:64)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.RescueNode.executeBody(RescueNode.java:224)
    org.jruby.ast.RescueNode.interpret(RescueNode.java:119)
    org.jruby.ast.BeginNode.interpret(BeginNode.java:83)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.ast.BlockNode.interpret(BlockNode.java:71)
    org.jruby.ast.WhileNode.interpret(WhileNode.java:131)
    org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    org.jruby.evaluator.ASTInterpreter.INTERPRET_BLOCK(ASTInterpreter.java:112)
    org.jruby.runtime.Interpreted19Block.evalBlockBody(Interpreted19Block.java:209)
    org.jruby.runtime.Interpreted19Block.yield(Interpreted19Block.java:197)
    org.jruby.runtime.Interpreted19Block.call(Interpreted19Block.java:128)
    org.jruby.runtime.Block.call(Block.java:89)
    org.jruby.RubyProc.call(RubyProc.java:261)
    org.jruby.RubyProc.call(RubyProc.java:213)
    org.jruby.internal.runtime.RubyRunnable.run(RubyRunnable.java:98)
    java.lang.Thread.run(Thread.java:722)

Rubinius, pools and HTTP causes strange behaviour

Hi - first, thanks for all the work on celluloid. Looks great!

We're hitting two problems when using celluloid, celluloid-io and http atop Rubinius. At a high level:

  1. Use of a pool with celluloid-io doesn't work as expected (although I will fully accept our expectations might be the thing that needs adjusting) - IO appears to become blocking
  2. A simple app crashes within the http gem (similar issue I believe to #84) due to a ArgumentError: Data object has already been freed error

The code we are using can be found in this gist. The steps to repro are as follows:

  • server.rb was run using MRI 2.0.0p353 using Puma
  • repro.rb was run using Rubinius (see system details below)
  • Gemfile for repro.rb

There are two output files that correspond to the following variations of this line of code in repro.rb:

server.rb contains a random sleep, hence in both cases our expectation was that all requests would be fired off immediately (puma runs with 16 threads by default and so all 10 can be handled simultaneously) and then return in a random order (which we see from the output of server.rb)

However what we see from scenario 1 is that only 4 requests are fired off initially, i.e. they appear to block, despite the actor including Celluloid::IO. Additionally this crashes with the 'ArgumentError: Data object has already been freed' error.

Scenario 2 behaves as expected from a threading perspective, but suffers a similar crash to scenario 1.

Any help much appreciated.

$ uname -a
Linux myitcv-virtual-machine 3.11.0-15-generic #23-Ubuntu SMP Mon Dec 9 18:17:04 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
$ ruby -v
rubinius 2.2.3 (2.1.0 4792e746 2013-12-29 JI) [x86_64-linux-gnu]
$ ruby -r 'celluloid' -e 'puts Celluloid.cores'
4

Versions of gems etc per Gemfile.lock

UDPSocket missing constructor parameter

Hello,

::UDPSocket.new([address_family]) => socket
vs.
Celluloid::IO::UDPSocket.new

By that, the address family cannot be specified up on creating Celluloid's UDP sockets and using IPv6 is not possible. :-/

Errno::ENOENT: No such file or directory - Y:/etc/resolv.conf

It seems like the Celluloid::IO DNS resolver is a bit Unix-only for the moment. This is what I get when trying to run it on JRuby for Windows (when using it with a DNS host name; specifying an IP address works correctly).

Errno::ENOENT: No such file or directory - Y:/etc/resolv.conf
        org/jruby/RubyFile.java:333:in `initialize'
        org/jruby/RubyIO.java:1179:in `open'
        org/jruby/RubyKernel.java:333:in `open'
        org/jruby/RubyIO.java:3611:in `read'
        org/jruby/RubyIO.java:3708:in `read'
        Y:/Work/Source/hg/eCraft.appFactory-2013.6/src/server/gems/jruby/1.9/gems/celluloid-io-0.14.0/lib/celluloid/io/dns_resolver.rb:19:in `nameservers'
        Y:/Work/Source/hg/eCraft.appFactory-2013.6/src/server/gems/jruby/1.9/gems/celluloid-io-0.14.0/lib/celluloid/io/dns_resolver.rb:36:in `initialize'
        Y:/Work/Source/hg/eCraft.appFactory-2013.6/src/server/gems/jruby/1.9/gems/celluloid-io-0.14.0/lib/celluloid/io/tcp_socket.rb:66:in `initialize'
        Y:/Work/Source/hg/eCraft.appFactory-2013.6/src/server/gems/jruby/1.9/gems/celluloid-websocket-client-0.0.1/lib/celluloid/websocket/client/connection.rb:
14:in `initialize'

celluloid-io nonblocking writes crash with 1.9.2/1.9.3 and hangs with jruby 1.6.7

# Tried out on: 1.9.2,1.9.3,jruby-1.6.7
#
# Usage:
# pty1$ ruby socket_hang.rb
# pty2$ nc localhost 55345
# then type something and press enter.
# it should respond to other messages after this too but it does not.
# If everything works - increase MULTI.
#
#1.9.2 and 1.9.3:
#
# E, [2012-04-03T21:47:55.651893 #24314] ERROR -- : ServerActor crashed!
# ArgumentError: this IO is already registered with selector
# /home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-io-0.10.0/lib/celluloid/io/reactor.rb:42:in `register'
# /home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-io-0.10.0/lib/celluloid/io/reactor.rb:42:in `wait'
# /home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-io-0.10.0/lib/celluloid/io/reactor.rb:26:in `wait_writable'
# /home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.10.0/lib/celluloid/calls.rb:56:in `dispatch'
# /home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.10.0/lib/celluloid/actor.rb:212:in `block in handle_message'
# /home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.10.0/lib/celluloid/task.rb:45:in `block in initialize'
#
# JRuby: just hangs there after first message, does not react to other messages.
#

require 'celluloid/io'

MULTI = 10

class ServerActor
  class StreamBuffer
    def initialize(separator="\n")
      @separator = separator
      @buffer = ""
    end

    def data(data)
      @buffer += data
    end

    def each_message
      newline_at = @buffer.index(@separator)
      until newline_at.nil?
        # Get our message.
        message = @buffer[0...newline_at]
        # Leave other part of buffer for further processing.
        @buffer = @buffer[(newline_at + 1)..-1]

        yield message

        newline_at = @buffer.index(@separator)
      end
    end
  end

  class Client
    attr_reader :host, :port

    def initialize(host, port)
      @host = host
      @port = port
    end

    def to_s
      "#{@host}:#{@port}"
    end
  end

  include Celluloid::IO

  def initialize(port)
    @server = Celluloid::IO::TCPServer.new("0.0.0.0", port)

    # client -> socket
    @sockets = {}

    run!
  end

  def to_s(client=nil)
    client.nil? ? "server" : "server-#{client}"
  end

  def info(msg, tag=nil); puts "inf #{tag} #{msg}"; end
  def debug(msg, tag=nil); puts "dbg #{tag} #{msg}"; end

  # Runs main loop which is responsible for accepting connections.
  def run
    info "Starting main event loop."
    loop do
      socket = @server.accept
      _, port, host = socket.peeraddr
      client = Client.new(host, port)

      handle! socket, client
    end
  end

  # Clean up upon actor exit.
  def finalize
    @server.close unless @server.nil?
  end

  # Handles one client connection.
  def handle(socket, client)
    @sockets[client] = socket
    info "Connected.", to_s(client)

    buffer = StreamBuffer.new

    msg_index = 0
    loop do
      # Read some data from the socket.
      data = socket.readpartial(4096)

      buffer.data(data)
      buffer.each_message do |message|
        if message == ""
          socket.write("ERROR: empty message\n")
          socket.close
          return
        end

        debug "Received message: \"#{message}\"", to_s(client)

        case msg_index
        when 0
          write!(client, 82)
          write!(client, 58)
          write!(client, 119)
          write!(client, 246662)
          write!(client, 513)
          write!(client, 863)
          write!(client, 89)
          write!(client, 503)
          write!(client, 404)
          write!(client, 90)
          write!(client, 295)
          write!(client, 580)
        else
          write!(client, 100)
        end

        msg_index += 1
      end
    end
  rescue EOFError, Errno::ECONNRESET
    # Our client has disconnected.
  ensure
    client_disconnected(client)
  end

  # Write _message_ serialized as JSON to socket associated with _client_.
  def write(client, length)
    socket = @sockets[client]
    if socket.nil?
      info "Message write aborted, socket not found:\n#{message}", to_s(client)
      return
    end

    data = "a" * length * MULTI

    debug "SA writing #{data.length}"
    socket.write "#{data}\n"
  rescue EOFError, IOError, Errno::EBADF, Errno::EPIPE
    # Our client has disconnected.
    client_disconnected(client)
  end

  def disconnect(client, message=nil)
    info "#disconnect called with #{message}", to_s(client)
    write(client, message) unless message.nil?

    socket = @sockets[client]
    socket.close if socket && ! socket.closed?
  end

  private
  def client_disconnected(client)
    @sockets.delete client
    info "Disconnected.", to_s(client)
  end
end

srv = ServerActor.new(55345)
puts "Running, press enter to exit"
gets

Actor Pool is not aware of Celluloid::IO Actors

When you call the pool method on Celluloid::IO actors, messages are not sent to the actors even when they are waiting on IO and hence idle. Basically they act like regular Celluloid actors when using pool.

Is there any way to detect when a Celluloid::IO actor is waiting on IO completion and hence idle? Currently I have created my own Actor pool and round-robin send messages to them which is a little naive

Unix signal handling (i.e. trap) is broken on Ruby 2.0

Ruby 2.0 p-195,
ruby examples/echo_server.rb
Ctrl-C right away, this message appears:

> /home/dragon/.rvm/gems/ruby-2.0.0-p195/gems/celluloid- 0.14.0/lib/celluloid/mailbox.rb:27:in `lock': can't be called from trap context (ThreadError)
>        from /home/dragon/.rvm/gems/ruby-2.0.0-p195/gems/celluloid-0.14.0/lib/celluloid/mailbox.rb:27:in `<<'
>        from /home/dragon/.rvm/gems/ruby-2.0.0-p195/gems/celluloid-0.14.0/lib/celluloid/proxies/actor_proxy.rb:96:in `terminate!'
>        from /home/dragon/.rvm/gems/ruby-2.0.0-p195/gems/celluloid-0.14.0/lib/celluloid/proxies/actor_proxy.rb:88:in `terminate'
>        from echoserver.rb:40:in `block in <main>'
>        from echoserver.rb:41:in `call'
>        from echoserver.rb:41:in `sleep'
>        from echoserver.rb:41:in `<main>'

Running the same program with 1.9.3p429, this program terminates gracefully. Don't know whether this is a bug or something, just let you author know it.

SSLSocket

Is there a timeline for getting SSLSocket implemented? I'm looking to get net:imap working with celluloid.

Celluloid::IO::ServerCell framework

I think it'd be good to do an extraction of the connection management logic that Reel uses into a generic, repurposable server framework for any kind of Celluloid::IO::Stream-derived server (i.e. TCP, TLS, or Unix domain)

Perhaps this framework could compose things correctly so we can generally address problems like celluloid/reel#92 for all Celluloid::IO-based servers.

I think this sort of high level framework that wraps up the connection management logic is the reason why people like frameworks like EventMachine and Node.js in the first place.

Celluloid pools break if Facter is unable to determine the CPU count

I can't figure out why this blows up

require 'celluloid'
require 'celluloid/io'

class Getter
  include Celluloid::IO
  def initialize
    @r = Celluloid::IO::DNSResolver.new
  end
  def address(a)
     s = @r.resolve(a).to_s 
     puts s
  end
end

def go
  oi = '111.74.82.33'
  ra = oi.split('.').reverse.join('.')
  addresses = ['stink.net','www.stink.net','logstash-dev.ssiem.us','ssiem.us',"#{ra}.dnsbl-1.uceprotect.net"]
  #r = Celluloid::IO::DNSResolver.new
  pool = Getter.pool
  addresses.each do |a|
    pool.address a
  end
end

this produces

E, [2012-11-13T03:33:28.897049 #1054] ERROR -- : Celluloid::PoolManager crashed!
NoMethodError: undefined method value' for nil:NilClass /home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid.rb:37:incores'
/home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/pool_manager.rb:12:in initialize' /home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/calls.rb:57:indispatch'
/home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/actor.rb:323:in block in handle_message' /home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/tasks/task_fiber.rb:22:inblock in initialize'
NoMethodError: undefined method value' for nil:NilClassE, [2012-11-13T03:33:28.898188 #1054] ERROR -- : Celluloid::PoolManager#finalize crashed! NoMethodError: undefined method+' for nil:NilClass
/home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/pool_manager.rb:26:in finalize' /home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/actor.rb:376:inblock in run_finalizer'
/home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/tasks/task_fiber.rb:22:in `block in initialize'

    from /home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid.rb:37:in `cores'
    from /home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/pool_manager.rb:12:in `initialize'
    from /home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/calls.rb:57:in `dispatch'
    from /home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/actor.rb:323:in `block in handle_message'
    from /home/vagrant/.rvm/gems/ruby-1.9.3-p286-perf/gems/celluloid-0.12.3/lib/celluloid/tasks/task_fiber.rb:22:in `block in initialize'

also I can't get jruby to work with celluloid-io

jruby-1.7.0 :001 > load './celluloid_tests.rb'
LoadError: load error: nio4r_ext -- java.lang.UnsatisfiedLinkError: failed to load shim library, error: /home/vagrant/.rvm/rubies/jruby-1.7.0/lib/native/x86_64-Linux/libjruby-cext.so: cannot open shared object file: No such file or directory
from org/jruby/RubyKernel.java:1019:in require' from /home/vagrant/.rvm/rubies/jruby-1.7.0/lib/ruby/shared/rubygems/custom_require.rb:36:inrequire'
from /home/vagrant/.rvm/gems/jruby-1.7.0/gems/nio4r-0.4.1/lib/nio.rb:19:in (root)' from org/jruby/RubyKernel.java:1019:inrequire'
from /home/vagrant/.rvm/rubies/jruby-1.7.0/lib/ruby/shared/rubygems/custom_require.rb:36:in require' from /home/vagrant/.rvm/gems/jruby-1.7.0/gems/celluloid-io-0.12.0/lib/celluloid/io/reactor.rb:1:in(root)'
from org/jruby/RubyKernel.java:1019:in require' from /home/vagrant/.rvm/rubies/jruby-1.7.0/lib/ruby/shared/rubygems/custom_require.rb:36:inrequire'
from /home/vagrant/.rvm/gems/jruby-1.7.0/gems/celluloid-io-0.12.0/lib/celluloid/io/reactor.rb:1:in (root)' from org/jruby/RubyKernel.java:1019:inrequire'
from /home/vagrant/.rvm/rubies/jruby-1.7.0/lib/ruby/shared/rubygems/custom_require.rb:60:in require' from /home/vagrant/.rvm/rubies/jruby-1.7.0/lib/ruby/shared/rubygems/custom_require.rb:55:inrequire'
from /home/vagrant/.rvm/gems/jruby-1.7.0/gems/celluloid-io-0.12.0/lib/celluloid/io.rb:1:in (root)' from org/jruby/RubyKernel.java:1045:inload'
from /home/vagrant/.rvm/gems/jruby-1.7.0/gems/celluloid-io-0.12.0/lib/celluloid/io.rb:8:in (root)' from org/jruby/RubyKernel.java:1065:ineval'
from /home/vagrant/ssiem/async-rep/./celluloid_tests.rb:1:in (root)' from /home/vagrant/ssiem/async-rep/./celluloid_tests.rb:2:in(root)'
from (irb):1:in (root)' from (irb):1:inevaluate'
from org/jruby/RubyKernel.java:1390:in loop' from org/jruby/RubyKernel.java:1173:incatch'
from org/jruby/RubyKernel.java:1173:in catch' from /home/vagrant/.rvm/rubies/jruby-1.7.0/bin/irb:13:in(root)'jruby-1

No popen support

Is there a counterpart to popen as the (broken) EventMachine::popen or is spawning processes seen differently in Celluloid?

Thank you

Server initiated socket.close for one connection terminates actor and all connections

Repository with the bug example and potential proposed tests to validate when the issue is resolved:

https://github.com/cognitiveflux/celluloid-io-socket-close-error

Using EchoServer.rb as a trivial example, when calling socket.close on a specific connection, the Celluloid::IO actor will terminate even though other sockets are still open. As a result, all connections are closed.

This problem originally surfaced when tracing why a server-side socket closure, for connections exceeding a timeout threshold, was closing all connections.

TCP Sockets and TIME_WAIT

When attempting to run a lot of connections against a Celluloid::IO Powered TCP Server after 2^14 (16384) connections the server has to wait for x minutes until TIME_WAIT connections get freed up again.

This is particularly noticeable in something like Reel using the Hello World example and running ab -n 20000 -c 50 http://127.0.0.1:5000/ it will effectively die at approx 2^14 requests.

I'm only opening this issue to investigate and either provide a suitable workaround or a documentation patch so others attempting to benchmark or heavily use Celluloid::IO know about it.

Mailbox receive timeout is not recalculated after IO events

From #52:

I reverted this patch as it changes the API of Mailbox#receive.
This causes more problems than it fixes.

I attempted to move the reactor into the actor.
https://github.com/halorgium/celluloid/compare/reactor-in-actor
https://github.com/halorgium/celluloid-io/compare/reactor-in-actor

I also have now attempted to wrap the reactor around the mailbox.

https://github.com/halorgium/celluloid/compare/reactor-around-mailbox
https://github.com/halorgium/celluloid-io/compare/reactor-around-mailbox

Limit the number of tasks active at a time in the same Actor

As far as I can tell there is no way currently to limit the number of fibers which can be spawned for each thread, is it something which is worked on ?
I may have a look into it but I think I remember reading something about some work done in the direction already.

I think the more straightforward way of adding such limit would be to use a fiber pool class of some sort to cap the number of fibers created.

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

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>'

Serial Port Support

Hi, I've created a gem (celluloid-io-serialport) that gives Celluloid::IO serial port support. It's available at: https://github.com/PrintToPeer/celluloid-io-serialport

I haven't been able to write tests yet due to the fact that I cooked my last surviving Arduino the other day, but I plan to write tests once my new one arrives. So far, I have connected it to a USB-to-TTL and have verified that writing works (blinking transmit light, nothing further).

Any feedback is greatly appreciated!

P.S. Thanks for bringing Erlang style and concepts to Ruby with Celluloid! 😄

Missing recv method on TCPSocket

I'm using Net::SCP inside a Celluloid::IO actor. It derives from Net::SSH, so initializing one is like initializing the other. Code like this:

class Loader
  include Celluloid::IO

  def load
    Net::SCP.start("localhost", ENV['USER'], proxy: TCPSocket) do |scp|
      scp.download!(File.join("somelocation.txt")
    end
  end
end

Loader.new.load

and then it happens:

Loader crashed!
NoMethodError: undefined method `recv' for #Celluloid::IO::TCPSocket:0x0000010a536f00

Since the issue propagates to the Net::SSH library, I wonder whether the problem is not actually inside the duck type.

Deadlock race condition when linking to actors after opening Socket on JRuby

We ran this against Celluloid::IO master, as well as 0.15.0 and 0.14.1 and they all reliably fail on this. When an Actor links to another actor after opening a Celluloid::IO::TCPSocket, the link seems to deadlock sometimes.

Using a regular TCPSocket fixes the problem, so does making a synchronous call to another actor after opening the socket. Linking the other actor before opening the socket is an easy work-around for the bug.

We needed some thread-safe counter, so we use Atomic for that. It is reproducible without it.

It fails on JRuby 1.7.6 and 1.7.4, it seems to succeed on MRI.

require "celluloid/io"
require "atomic"

class A
  include Celluloid::IO

  class B
    include Celluloid
    def foo; end
  end

  def listen
    # B.new_link # this works!
    socket = TCPSocket.new("www.google.com", 80)
    $reference.update { |x| x + 1 }

    # B.new.foo # for some reason, this solves the problem too
    B.new_link # this does not work!
    $counter.update { |x| x + 1 }
  end
end

$counter = Atomic.new(0)
$reference = Atomic.new(0)
100.times { A.new.async.listen }

puts "Wait 5 seconds."
sleep(5)
puts "Total: #{$counter.value} of #{$reference.value}"

Expected output:

Wait 5 seconds.
Total: 100 of 100

Actual output:

Wait 5 seconds.
Total: 33 of 100

/cc @Burgestrand

IOError: closed stream

Coming from here: https://github.com/tarcieri/nio4r/issues/18
Reproduction: https://gist.github.com/romansergey/5103388

I've tried with the git versions of celluloid-io, celluloid and reel and it also crashes:

E, [2013-03-07T08:40:10.454000 #20567] ERROR -- : MyServer crashed!
IOError: closed stream
    org/nio4r/Nio4r.java:143:in `register'
    /home/rs/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-ebc8464622e6/lib/celluloid/io/reactor.rb:42:in `wait'
    /home/rs/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-io-ebc8464622e6/lib/celluloid/io/reactor.rb:21:in `wait_readable'
    org/jruby/RubyBasicObject.java:1665:in `__send__'
    org/jruby/RubyKernel.java:1809:in `public_send'
    /home/rs/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-adbbbd850998/lib/celluloid/calls.rb:11:in `dispatch'
    /home/rs/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-adbbbd850998/lib/celluloid/calls.rb:53:in `dispatch'
    /home/rs/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-adbbbd850998/lib/celluloid/actor.rb:328:in `handle_message'
    /home/rs/.rvm/gems/jruby-1.7.2/bundler/gems/celluloid-adbbbd850998/lib/celluloid/tasks/task_fiber.rb:28:in `initialize'
D, [2013-03-07T08:40:10.504000 #20567] DEBUG -- : Terminating 19 actors...
D, [2013-03-07T08:40:10.577000 #20567] DEBUG -- : Shutdown completed cleanly

Segfault with more than 255 sockets

Hey guys! When I run the following code:

#!/usr/bin/ruby1.9.1

require 'rubygems'
require 'celluloid/io'

class Client
  include Celluloid::IO
  def initialize(host, port) @socket = TCPSocket.new(host, port) end
  def read() @socket.readpartial(100) end
 end

for i in 0..255 do
  client = Client.new("127.0.0.1", 1234)
  puts client.read() 
end

I get a segfault:

/var/lib/gems/1.9.1/gems/celluloid-io-0.12.0/lib/celluloid/io/reactor.rb:16: [BUG] Segmentation fault
ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-linux]

-- control frame ----------
c:0020 p:---- s:0060 b:0060 l:000059 d:000059 CFUNC  :(null)
c:0019 p:---- s:0058 b:0058 l:000057 d:000057 CFUNC  :new
c:0018 p:0019 s:0055 b:0055 l:000054 d:000054 METHOD /var/lib/gems/1.9.1/gems/celluloid-io-0.12.0/lib/celluloid/io/reactor.rb:16
c:0017 p:---- s:0052 b:0052 l:000051 d:000051 FINISH
c:0016 p:---- s:0050 b:0050 l:000049 d:000049 CFUNC  :new
c:0015 p:0051 s:0047 b:0047 l:000046 d:000046 METHOD /var/lib/gems/1.9.1/gems/celluloid-io-0.12.0/lib/celluloid/io/mailbox.rb:10
c:0014 p:---- s:0043 b:0043 l:000042 d:000042 FINISH
c:0013 p:---- s:0041 b:0041 l:000040 d:000040 CFUNC  :new
c:0012 p:0011 s:0038 b:0038 l:001750 d:000037 BLOCK  /var/lib/gems/1.9.1/gems/celluloid-0.12.0/lib/celluloid.rb:142
c:0011 p:---- s:0036 b:0036 l:000035 d:000035 FINISH
c:0010 p:---- s:0034 b:0034 l:000033 d:000033 CFUNC  :call
c:0009 p:0025 s:0031 b:0031 l:000030 d:000030 METHOD /var/lib/gems/1.9.1/gems/celluloid-0.12.0/lib/celluloid.rb:171
c:0008 p:0013 s:0028 b:0027 l:000026 d:000026 METHOD /var/lib/gems/1.9.1/gems/celluloid-0.12.0/lib/celluloid.rb:182
c:0007 p:0025 s:0024 b:0022 l:000021 d:000021 METHOD /var/lib/gems/1.9.1/gems/celluloid-0.12.0/lib/celluloid.rb:78
c:0006 p:0025 s:0016 b:0016 l:002688 d:000015 BLOCK  ./client.rb:13
c:0005 p:---- s:0013 b:0013 l:000012 d:000012 FINISH
c:0004 p:---- s:0011 b:0011 l:000010 d:000010 CFUNC  :each
c:0003 p:0044 s:0008 b:0008 l:002688 d:000c58 EVAL   ./client.rb:12
c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH
c:0001 p:0000 s:0002 b:0002 l:002688 d:002688 TOP   
---------------------------
-- Ruby level backtrace information ----------------------------------------
./client.rb:12:in `<main>'
./client.rb:12:in `each'
./client.rb:13:in `block in <main>'
/var/lib/gems/1.9.1/gems/celluloid-0.12.0/lib/celluloid.rb:78:in `new'
/var/lib/gems/1.9.1/gems/celluloid-0.12.0/lib/celluloid.rb:182:in `actor_options'
/var/lib/gems/1.9.1/gems/celluloid-0.12.0/lib/celluloid.rb:171:in `mailbox_factory'
/var/lib/gems/1.9.1/gems/celluloid-0.12.0/lib/celluloid.rb:171:in `call'
/var/lib/gems/1.9.1/gems/celluloid-0.12.0/lib/celluloid.rb:142:in `block in mailbox_class'
/var/lib/gems/1.9.1/gems/celluloid-0.12.0/lib/celluloid.rb:142:in `new'
/var/lib/gems/1.9.1/gems/celluloid-io-0.12.0/lib/celluloid/io/mailbox.rb:10:in `initialize'
/var/lib/gems/1.9.1/gems/celluloid-io-0.12.0/lib/celluloid/io/mailbox.rb:10:in `new'
/var/lib/gems/1.9.1/gems/celluloid-io-0.12.0/lib/celluloid/io/reactor.rb:16:in `initialize'
/var/lib/gems/1.9.1/gems/celluloid-io-0.12.0/lib/celluloid/io/reactor.rb:16:in `new'
zsh: segmentation fault  ./client.rb

When I change 255 to 254, everything works as expected.

Am I missing something or is this is a bug?

Thanks!
Flo

Resolv::ResolvError for hosts resolved by /etc/resolver/* files on OS X

I'm experiencing inconsistent DNS resolution between initializing a new Celluloid::IO::TCPSocket and initializing a regular TCPSocket:

TCPSocket.new 'project.dev', 80
# => #<TCPSocket:fd 7>

Celluloid::IO::TCPSocket.new 'project.dev', 80
# Resolv::ResolvError: DNS result has no information for project.dev
# from /opt/boxen/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/celluloid-io-0.16.0.pre/lib/celluloid/io/tcp_socket.rb:67:in `initialize'

I'm not entirely certain what the difference in host resolution is between the two implementations. Socket.gethostbyname works fine while Resolve.getaddress fails similarly to Celluloid::IO::DNSResolver#resolve.

Boxen uses dnsmasq to set up a *.dev wildcard that resolves to 127.0.0.1. The interesting thing however, is that the nameserver is specified in /etc/resolver/dev, what appears to be an OS X-specific format where the name of the file is the TLD and the contents is a regular /etc/resolv.conf definition:

$ cat /etc/resolv.conf
nameserver 10.0.1.1 # a nameserver that does not resolve *.dev hosts

$ cat /etc/resolver/dev
nameserver 127.0.0.1 # a nameserver that *does* resolve *.dev hosts

Anyway, I'm not sure what the project's stance is in regard to supporting odd platform-specific setups (there's only enough room for Windows to do that!) but I'm curious what the reason is for resolution to be working fine everywhere else.

Thanks!

win7/32 - busted running EchoServer example

starts fine but when I connect to it, it crashes..
Also tested code on Ubuntu, works fine..

any ideas...

E, [2013-07-04T10:35:42.466017 #3800] ERROR -- : EchoServer crashed!
ThreadError: deadlock; recursive locking
internal:prelude:8:in lock' <internal:prelude>:8:insynchronize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/nio4r-0.4.6/lib/nio/selector.rb:34:i
n deregister' C:/Ruby193/lib/ruby/gems/1.9.1/gems/nio4r-0.4.6/lib/nio/monitor.rb:40:in close'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-io-0.14.1/lib/celluloid/io
/reactor.rb:51:in block in run_once' C:/Ruby193/lib/ruby/gems/1.9.1/gems/nio4r-0.4.6/lib/nio/selector.rb:88:i nblock (2 levels) in select'
C:/Ruby193/lib/ruby/1.9.1/set.rb:222:in block in each' C:/Ruby193/lib/ruby/1.9.1/set.rb:222:ineach_key'
C:/Ruby193/lib/ruby/1.9.1/set.rb:222:in each' C:/Ruby193/lib/ruby/gems/1.9.1/gems/nio4r-0.4.6/lib/nio/selector.rb:87:i nblock in select'
internal:prelude:10:in synchronize' C:/Ruby193/lib/ruby/gems/1.9.1/gems/nio4r-0.4.6/lib/nio/selector.rb:48:i nselect'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-io-0.14.1/lib/celluloid/io
/reactor.rb:49:in run_once' C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/event ed_mailbox.rb:56:inreceive'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor
.rb:173:in run' C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor .rb:157:inblock in initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/threa
d_handle.rb:13:in block in initialize' C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/inter nal_pool.rb:59:incall'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/inter
nal_pool.rb:59:in `block in create'

Missing #getsockname on TCPSockets

I'm using celluloid 0.15.0 and net-scp with net-ssh (v2.7.0). When I try to start an SCP connection i get the following stacktrace:

        NameError: undefined local variable or method `getsockname' for #<Celluloid::IO::TCPSocket:0x0000000451dd68>
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/transport/packet_stream.rb:41:in `client_name'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/authentication/methods/hostbased.rb:29:in `hostname'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/authentication/methods/hostbased.rb:38:in `authenticate_with'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/authentication/methods/hostbased.rb:18:in `block in authenticate'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/authentication/key_manager.rb:121:in `block in each_identity'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/authentication/key_manager.rb:118:in `each'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/authentication/key_manager.rb:118:in `each_identity'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/authentication/methods/hostbased.rb:17:in `authenticate'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/authentication/session.rb:78:in `block in authenticate'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/authentication/session.rb:65:in `each'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh/authentication/session.rb:65:in `authenticate'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-ssh-2.7.0/lib/net/ssh.rb:204:in `start'
        /srv/www/rails/viktor/shared/bundle/ruby/1.9.1/gems/net-scp-1.1.2/lib/net/scp.rb:197:in `start'

Apparently what is missing is this method in the Cell IO sockets:

http://ruby-doc.org/stdlib-1.9.3/libdoc/socket/rdoc/BasicSocket.html#method-i-getsockname

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

wait_readable inside Celluloid::Actor#timeout not unregistering the IO descriptor

I'm having an issue using the wait_readable inside the timeout handler. I have something resembling this code:

 # inside actor
  def wait_readable(t)
     timeout(t) do
       @sock.wait_readable
     end
  end

I have this case in which the wait_readable blocks and the timeout is triggered. I'm thrown a Celluloid::Task::TimeoutError exception.

The problem is, right afterwards some other method calls @sock.wait_writable and I get the following exception:
this IO is already registered with selector

this only happens in such a case. Can it be that the timeout trigger handling is not taking into account the possibly registered sockets?

invalid byte sequence in US-ASCII

Hi,

im playing with celluloid and libwebsocket,
im triyn to run a basic example but im receiving errors from a method of libwebsocket, probably the error is from there, but i have run the tests of it and all pass.

the conflict line is when in message.rb (from libwebsocket) calls a slice method : @buffer.slice!(/\A(.*?)\x0d?\x0a/) where buffer is the received_data http data.

i have run the same test in a normal TCPServer and i dont get this error, so i think it might be related to some http parsing from celluloid or celluloid-io, what do you think ?

you can see the intent here https://github.com/michelson/ws-celluloid

regards

Exceptions when a TCPSocket connection timed out in and out of the Reactor using Kernel.timeout

I'm fighting against the non-establishment of connections using Celluloid::IO::TCPSocket s. Currently my issue is with connections timing out.

Let's say I have an "timeoutable" hostname.

Celluloid::IO::TCPSocket.new("timeoutable", 23)
(10 seconds later...)
Errno::ETIMEDOUT: Connection timed out - connect(2)

Which is consistent with what happens when using a regular TCPSocket.

Now, when I do that inside an actor...

Celluloid A
  include Celluloid::IO
  def establish
    timeout(5) do
      @tcp = TCPSocket.new("timeoutable", 23)
    end
  end
end
a.establish

It raises a Celluloid::Task::TimeoutError, which makes sense (but it kills the actor).

Now, other case:

Celluloid A
  include Celluloid::IO
  def establish
    Kernel.send(:timeout, 5) do
      @tcp = TCPSocket.new("timeoutable", 23)
    end
  end
end
a.establish

This one raises an "execution expired" exception coming from the reactor. And kills the actor.

So, there are two issues for me. Let's say I have an actor whose task is to perform a task on x remote devices. Being that for some I cannot connect on time, how can I rescue the connection timeout and not kill the actor in the process, but simple handle the logic, mark the remote location as unreachable and move on with my actor life? The way I tested, I can rescue Errno::ETIMEDOUT and Celluloid::Task::TimeoutError on the method and keep the actor alive. But I cannot rescue the third one, because that exception is thrown after the actor has been killed, so no way to keep him alive.

The second is regarding that last actor. As you have seen, I have used Kernel.timeout inside the reactor loop, which clearly doesn't play along very well. I know your first question is "uh, why you used Kernel.timeout instead of Actor#timeout as in the second example?", but I'm using a wrapper whom I pass a proxy. This wrapper doesn't know anything about Celluloid, hence it calls normal. timeout. i don't have any influence. As I've seen, I can only use this timeout inside the actor (an instance method, therefore). How could I access the current actor and call the #timeout method in such a case?

I hope the second question can lead to an answer of the first one :)

TCPSocket#read length parameter should be optional

The Celluloid::IO:CommonMethods#read method requires the length parameter to be defined, and the implementation requires it. The problem is that the core IO#read method accepts a nil length, in which case the stream must be read until EOF.

This breaks compatibility with the original implementation.

I found this difference while playing with TCPSocket and BinData, with some code like:

class Message < BinData::Record
...
end

class Server
include Celluloid::IO
...

def handle_connection(sock)
message = Message.read(sock)
...
end
end

Celluloid-IO crashes with jruby 1.6.7/mri 1.9.2/1.9.3

arturas@zeus:~/work/jruby$ rvm use 1.9.2
Using /home/arturas/.rvm/gems/ruby-1.9.2-p318
arturas@zeus:~/work/jruby$ ruby include_bug.rb 
1.9.2
non-jruby
*** Starting echo server on localhost:12345
Running!
*** Received connection from 127.0.0.1:54507
E, [2012-03-31T20:20:29.394655 #14346] ERROR -- : EchoServer crashed!
NoMethodError: undefined method `wait_writable' for #<Celluloid::Actor(EchoServer:0x438a0b4) @server=#<Celluloid::IO::TCPServer:0x8713aec @server=#<TCPServer:fd 6>>>
/home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.9.1/lib/celluloid/calls.rb:12:in `check_signature'
/home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.9.1/lib/celluloid/calls.rb:49:in `dispatch'
/home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.9.1/lib/celluloid/actor.rb:209:in `block in handle_message'
/home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.9.1/lib/celluloid/task.rb:45:in `block in initialize'
E, [2012-03-31T20:20:29.395191 #14346] ERROR -- : Client crashed!
NoMethodError: undefined method `wait_writable' for #<Celluloid::Actor(Client:0x4389b8c)>
/home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.9.1/lib/celluloid/calls.rb:12:in `check_signature'
/home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.9.1/lib/celluloid/calls.rb:49:in `dispatch'
/home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.9.1/lib/celluloid/actor.rb:209:in `block in handle_message'
/home/arturas/.rvm/gems/ruby-1.9.2-p318/gems/celluloid-0.9.1/lib/celluloid/task.rb:45:in `block in initialize'

Repro:

# To run:
# $ gem install celluloid celluloid-io
# $ export JRUBY_OPTS=--1.9
# $ ruby include_bug.rb

puts RUBY_VERSION
puts defined?(JRUBY_VERSION) ? JRUBY_VERSION : "non-jruby"
require 'celluloid'
require 'celluloid/io'

class EchoServer
  include Celluloid::IO

  def initialize(host, port)
    puts "*** Starting echo server on #{host}:#{port}"

    @server = Celluloid::IO::TCPServer.new(host, port)
    run!
  end

  def finalize
    @server.close if @server
  end

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

  def handle_connection(socket)
    _, port, host = socket.peeraddr
    puts "*** Received connection from #{host}:#{port}"
    loop { socket.write socket.readpartial(4096) }
  rescue EOFError
    puts "*** #{host}:#{port} disconnected"
    socket.close
  end
end

class Client
  include Celluloid::IO

  def initialize
    run!
  end

  def run
    c = Celluloid::IO::TCPSocket.new("localhost", 12345)
    c.write("x" * 1_000_000)
    puts "Written. Press enter to end."
  end
end

s = EchoServer.new("localhost", 12345)
c = Client.new

puts "Running!"
gets
c.terminate
s.terminate

Rubinius 2.0.0rc1: NoMethodError on Celluloid::IO::TCPSocket

Found during the course of using Reel... it seems like this happened while building a Response, because the Request seemed to go through, but this doesn't appear to be a Reel issue, but Celluloid::IO.

rubinius 2.0.0.rc1 (1.9.3 a0118d45 yyyy-mm-dd JI) [x86_64-unknown-linux-gnu]

E, [2013-04-16T12:05:16.232885 #19043] ERROR -- : Reel::RackWorker crashed!
NoMethodError: undefined method `ensure_open_and_writable' on an instance of Celluloid::IO::TCPSocket.
    kernel/delta/kernel.rb:81:in `ensure_open_and_writable (method_missing)'
    kernel/common/io19.rb:282:in `run'
    kernel/common/io19.rb:320:in `copy_stream'
    /mu/rack-reel/reel/lib/reel/response.rb:69:in `render'
    /mu/rack-reel/reel/lib/reel/connection.rb:132:in `respond'
    /mu/rack-reel/reel/lib/reel/rack_worker.rb:75:in `handle_request'
    /mu/rack-reel/reel/lib/reel/rack_worker.rb:65:in `handle'
    /usr/local/rvm/gems/rbx-head/bundler/gems/celluloid-b5488427f600/lib/celluloid/calls.rb:25:in `dispatch'
    /usr/local/rvm/gems/rbx-head/bundler/gems/celluloid-b5488427f600/lib/celluloid/calls.rb:67:in `dispatch'
    /usr/local/rvm/gems/rbx-head/bundler/gems/celluloid-b5488427f600/lib/celluloid/actor.rb:322:in `handle_message'
    /usr/local/rvm/gems/rbx-head/bundler/gems/celluloid-b5488427f600/lib/celluloid/tasks.rb:44:in `initialize'
    /usr/local/rvm/gems/rbx-head/bundler/gems/celluloid-b5488427f600/lib/celluloid/tasks/task_fiber.rb:9:in `create'
    kernel/bootstrap/proc.rb:22:in `call'

Just happened to test rbx to see if http_parser.rb performed differently outside jRuby.

Subclasses of Celluloid::IO actor classes may not cleanly terminate

Library version: celluloid-io 0.11.0
OS: Mac OS X 10.6.8
Tested Ruby environments:

rubinius 2.0.0dev (1.9.3 62d48b11 yyyy-mm-dd JI) [x86_64-apple-darwin10.8.0]
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin10.8.0]

When a instance of a subclass of a Celluloid::IO actor is waiting, that actor does not respond to Celluloid's termination procedures.

An example: https://gist.github.com/3014842#file_example.rb. Under Rubinius 2.0, sending INT to the Ruby process will cause the stream of "hi"s and "bye"s to halt, but the actors will not terminate; repeated INTs will eventually terminate the process. Under Ruby 1.9.3-p194, the actors do not terminate and the only way to terminate the process is via KILL.

The problem appears to be related to the subclass' mailbox not having the customizations present in Celluloid::IO::Mailbox, as evented? on the socket in an instance of the subclass returns false.

Including Celluloid::IO in the subclass restores expected termination behavior, as does installing an inherited hook in the base class: https://gist.github.com/3014842#file_this_terminates.rb

Provide a more sensible way to write servers/clients

While I think it's a worthwhile goal to provide a TCPServer/TCPSocket-compatible IO-interface, it's not something I want to write my servers/clients in.

Celluloid::IO (or maybe it should be nio4r?) definitely needs a more sensible (and maybe opinionated) abstraction where you can write 90% of all servers.

ConcurrencyError in celluloid/io/mailbox.rb

I've got this while my server was running. Not quite sure what to make out of it.

Server has encountered an error!

[2012-04-09 11:34:00.620|worker-world_0|main|error] Threading::Worker crashed!
ConcurrencyError: Detected invalid array contents due to unsynchronized modifications with concurrent users
org/jruby/RubyArray.java:1155:in `<<'
/home/spacegame/nebula-server/20120406132608/vendor/bundle/jruby/1.9/gems/celluloid-io-0.10.0/lib/celluloid/io/mailbox.rb:17:in `<<'
/home/spacegame/nebula-server/20120406132608/vendor/bundle/jruby/1.9/gems/celluloid-io-0.10.0/lib/celluloid/io/mailbox.rb:16:in `<<'
/home/spacegame/nebula-server/20120406132608/vendor/bundle/jruby/1.9/bundler/gems/celluloid-62f219250e08/lib/celluloid/actor.rb:66:in `async'
/home/spacegame/nebula-server/20120406132608/vendor/bundle/jruby/1.9/bundler/gems/celluloid-62f219250e08/lib/celluloid/actor_proxy.rb:74:in `method_missing'
/home/spacegame/nebula-server/20120406132608/lib/server/dispatcher.rb:466:in `transmit_to_client'
/home/spacegame/nebula-server/20120406132608/lib/server/dispatcher.rb:201:in `respond'
org/jruby/RubyBasicObject.java:1706:in `__send__'
org/jruby/RubyKernel.java:2103:in `send'
/home/spacegame/nebula-server/20120406132608/vendor/bundle/jruby/1.9/bundler/gems/celluloid-62f219250e08/lib/celluloid/calls.rb:98:in `dispatch'
/home/spacegame/nebula-server/20120406132608/vendor/bundle/jruby/1.9/bundler/gems/celluloid-62f219250e08/lib/celluloid/actor.rb:223:in `handle_message'
/home/spacegame/nebula-server/20120406132608/vendor/bundle/jruby/1.9/bundler/gems/celluloid-62f219250e08/lib/celluloid/task.rb:45:in `initialize'
/home/spacegame/nebula-server/20120406132608/vendor/bundle/jruby/1.9/bundler/gems/celluloid-62f219250e08/lib/celluloid/task.rb:44:in `initialize'

Celluloid::IO::SSLSocket.accept ?

I added this method to a WIP project i'm dabbling with, is it something you want in Celluloid::IO proper? If so I can put together a pull request with some tests.

https://github.com/codekitchen/tkellem/blob/celluloid/lib/tkellem/celluloid_tools.rb#L32

The advantage of putting the SSL accept on the socket, rather than having a Celluloid::IO::SSLServer is that you can send data on the socket before "upgrading" it to SSL, STARTTLS style. It'd be convenient to add a SSLServer class as well, that calls accept on the SSL socket immediately on connection.

Celluloid::IO does not respect terminate when reading from a buffer that is always full

/cc @jnicklas

Here’s a gist of a reproduction case: https://gist.github.com/Burgestrand/7421e529fd92a4889afd

Some caveats about the code in the gist. I tried it on:

  • CRuby 2.0.0-p195
  • JRuby 1.7.4
  • JRuby 1.7.6

For celluloid / celluloid io versions:

  • 0.15.2 / 0.15.0
  • 0.14.1 / 0.14.1

With the following results:

  • CRuby does not work at all. Reader receives connection refused. Not sure why, probably I’m doing something wrong.
  • JRuby 1.7.4 does not terminate the actors, but ^C will shut it down because the JVM shuts down hard.
  • JRuby 1.7.6 does not terminate the actors, and ^C will not shut it down. It will reach the last line of code (reader.terminate returns), but neither the reader nor writer will terminate. Must kill -9, so probably Celluloid.shutdown not working properly here either.

Version of celluloid does not change the results, except 0.15.x will whine more in the log about terminated tasks.

Celluloid::IO 0.15.0 incompatible with Net::SSH 2.8.x

Just tried using Net::SSH connection with Celluloid::IO::TCPSocket using the latest versions and it failed. It seems that it passes an hash to the third argument of the TCPSocket constructor, which is expected to be a String by Celluloid::IO.

I downgraded to version 2.0.14 of Net::SSH and everything worked.

IO::TcpServer#addr is missing

TcpServer implementation in the standard library includes an #addr method, allowing code like this:

server = TcpServer.new(0)
port = server.addr[1]

The celluloid-io implementation of TcpServer is missing the #addr method.

Thanks for a great library!

Errno::ETIMEDOUT not being raised properly.

See PR #61 which is still failing, with stack trace at the top.

Basically, Celluloid::IO::Stream which TCPSocket and UNIXSocket mixin do not properly raise Errno::ETIMEDOUT on read or readpartial.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.