celluloid-io 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format documentation
3
+ --backtrace
4
+ --default_path spec
@@ -0,0 +1,8 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ # - rbx # crashing :(
5
+ - jruby
6
+ - ruby-head
7
+
8
+ env: "JRUBY_OPTS=--1.9"
@@ -0,0 +1,3 @@
1
+ 0.7.0
2
+ -----
3
+ * Initial release forked from Celluloid
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ gem 'celluloid', :git => 'git://github.com/tarcieri/celluloid'
4
+
5
+ # Specify your gem's dependencies in celluloid-io.gemspec
6
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Tony Arcieri
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,80 @@
1
+ Celluloid::IO
2
+ =============
3
+ [![Build Status](http://travis-ci.org/tarcieri/celluloid-io.png)](http://travis-ci.org/tarcieri/celluloid-io)
4
+
5
+ You don't have to choose between threaded and evented IO! Celluloid::IO provides
6
+ a simple and easy way to wait for IO events inside of a Celluloid actor, which
7
+ runs in its own thread. Any Ruby IO object can be registered and monitored.
8
+ It's a somewhat similar idea to Ruby event frameworks like EventMachine and
9
+ Cool.io, but Celluloid actors automatically wrap up all IO in Fibers,
10
+ resulting in a synchronous API that's duck type compatible with existing IO
11
+ objects.
12
+
13
+ Unlike EventMachine, you can make as many Celluloid::IO actors as you wish,
14
+ each running their own event loop independently from the others. Using many
15
+ actors allows your program to scale across multiple CPU cores on Ruby
16
+ implementations which don't have a GIL, such as JRuby and Rubinius.
17
+
18
+ Like Celluloid::IO? [Join the Google Group](http://groups.google.com/group/celluloid-ruby)
19
+
20
+ Supported Platforms
21
+ -------------------
22
+
23
+ Celluloid works on Ruby 1.9.2+, JRuby 1.6 (in 1.9 mode), and Rubinius 2.0. JRuby
24
+ or Rubinius are the preferred platforms as they support true hardware-level
25
+ parallelism when running Ruby code, whereas MRI/YARV is constrained by a global
26
+ interpreter lock (GIL).
27
+
28
+ To use JRuby in 1.9 mode, you'll need to pass the "--1.9" command line option
29
+ to the JRuby executable, or set the "JRUBY_OPTS=--1.9" environment variable.
30
+
31
+ Celluloid works on Rubinius in either 1.8 or 1.9 mode.
32
+
33
+ Usage
34
+ -----
35
+
36
+ To use Celluloid::IO, define a normal Ruby class that includes Celluloid::IO:
37
+
38
+ require 'celluloid/io'
39
+
40
+ class MyServer
41
+ include Celluloid::IO
42
+
43
+ # Bind a TCP server to the given host and port
44
+ def initialize(host, port)
45
+ @server = TCPServer.new host, port
46
+ run!
47
+ end
48
+
49
+ # Run the TCP server event loop
50
+ def run
51
+ while true
52
+ wait_readable(@server)
53
+ on_connect @server.accept
54
+ end
55
+ end
56
+
57
+ # Terminate this server
58
+ def terminate
59
+ @server.close
60
+ super
61
+ end
62
+
63
+ # Called whenever a new connection is opened
64
+ def on_connect(connection)
65
+ connection.close
66
+ end
67
+ end
68
+
69
+ Contributing to Celluloid::IO
70
+ -----------------------------
71
+
72
+ * Fork Celluloid on github
73
+ * Make your changes and send me a pull request
74
+ * If I like them I'll merge them and give you commit access to my repository
75
+
76
+ License
77
+ -------
78
+
79
+ Copyright (c) 2011 Tony Arcieri. Distributed under the MIT License. See
80
+ LICENSE.txt for further details.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :default => :spec
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/celluloid/io/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Tony Arcieri"]
6
+ gem.email = ["tony.arcieri@gmail.com"]
7
+ gem.description = "Evented IO for Celluloid actors"
8
+ gem.summary = "Celluloid::IO allows you to monitor multiple IO objects within a Celluloid actor"
9
+ gem.homepage = "http://github.com/tarcieri/celluloid-io"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "celluloid-io"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Celluloid::IO::VERSION
17
+
18
+ gem.add_development_dependency('celluloid', '>= 0.7.0')
19
+
20
+ gem.add_development_dependency('rake')
21
+ gem.add_development_dependency('rspec', ['>= 2.7.0'])
22
+ end
@@ -0,0 +1,27 @@
1
+ require 'celluloid/io/version'
2
+
3
+ require 'celluloid'
4
+ require 'celluloid/io/mailbox'
5
+ require 'celluloid/io/reactor'
6
+ require 'celluloid/io/waker'
7
+
8
+ module Celluloid
9
+ # Actors with evented IO support
10
+ module IO
11
+ def self.included(klass)
12
+ klass.send :include, Celluloid
13
+ klass.use_mailbox Celluloid::IO::Mailbox
14
+ end
15
+
16
+ # Wait for the given IO object to become readable
17
+ def wait_readable(io, &block)
18
+ # Law of demeter be damned!
19
+ current_actor.mailbox.reactor.wait_readable(io, &block)
20
+ end
21
+
22
+ # Wait for the given IO object to become writeable
23
+ def wait_writeable(io, &block)
24
+ current_actor.mailbox.reactor.wait_writeable(io, &block)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,72 @@
1
+ module Celluloid
2
+ module IO
3
+ # An alternative implementation of Celluloid::Mailbox using Wakers
4
+ class Mailbox < Celluloid::Mailbox
5
+ attr_reader :reactor, :waker
6
+
7
+ def initialize
8
+ @messages = []
9
+ @lock = Mutex.new
10
+ @waker = Waker.new
11
+ @reactor = Reactor.new(@waker)
12
+ end
13
+
14
+ # Add a message to the Mailbox
15
+ def <<(message)
16
+ @lock.synchronize do
17
+ @messages << message
18
+ @waker.signal
19
+ end
20
+ nil
21
+ rescue DeadWakerError
22
+ raise MailboxError, "dead recipient"
23
+ end
24
+
25
+ # Add a high-priority system event to the Mailbox
26
+ def system_event(event)
27
+ @lock.synchronize do
28
+ @messages.unshift event
29
+
30
+ begin
31
+ @waker.signal
32
+ rescue DeadWakerError
33
+ # Silently fail if messages are sent to dead actors
34
+ end
35
+ end
36
+ nil
37
+ end
38
+
39
+ # Receive a message from the Mailbox
40
+ def receive(timeout = nil, &block)
41
+ message = nil
42
+
43
+ begin
44
+ if timeout
45
+ now = Time.now
46
+ wait_until ||= now + timeout
47
+ wait_interval = wait_until - now
48
+ return if wait_interval < 0
49
+ else
50
+ wait_interval = nil
51
+ end
52
+
53
+ @reactor.run_once(wait_interval) do
54
+ @waker.wait
55
+ message = next_message(&block)
56
+ end
57
+ end until message
58
+
59
+ message
60
+ rescue IOError, DeadWakerError
61
+ shutdown # force shutdown of the mailbox
62
+ raise MailboxShutdown, "mailbox shutdown called during receive"
63
+ end
64
+
65
+ # Cleanup any IO objects this Mailbox may be using
66
+ def shutdown
67
+ @waker.cleanup
68
+ super
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,63 @@
1
+ module Celluloid
2
+ module IO
3
+ # React to external I/O events. This is kinda sorta supposed to resemble the
4
+ # Reactor design pattern.
5
+ class Reactor
6
+ def initialize(waker)
7
+ @waker = waker
8
+ @readers = {}
9
+ @writers = {}
10
+ end
11
+
12
+ # Wait for the given IO object to become readable
13
+ def wait_readable(io)
14
+ wait_for_io io, @readers
15
+ end
16
+
17
+ # Wait for the given IO object to become writeable
18
+ def wait_writeable(io)
19
+ wait_for_io io, @writers
20
+ end
21
+
22
+ # Run the reactor, waiting for events, and calling the given block if
23
+ # the reactor is awoken by the waker
24
+ def run_once(timeout = nil)
25
+ readers, writers = select(@readers.keys << @waker.io, @writers.keys, [], timeout)
26
+ return unless readers
27
+
28
+ yield if readers.include? @waker.io
29
+
30
+ [[readers, @readers], [writers, @writers]].each do |ios, registered|
31
+ ios.each do |io|
32
+ task = registered.delete io
33
+ task.resume if task
34
+ end
35
+ end
36
+ end
37
+
38
+ #######
39
+ private
40
+ #######
41
+
42
+ def wait_for_io(io, set)
43
+ # zomg ugly type conversion :(
44
+ unless io.is_a?(IO)
45
+ if IO.respond_to? :try_convert
46
+ io = IO.try_convert(io)
47
+ elsif io.respond_to? :to_io
48
+ io = io.to_io
49
+ else raise TypeError, "can't convert #{io.class} into IO"
50
+ end
51
+ end
52
+
53
+ if set.has_key? io
54
+ raise ArgumentError, "another method is already waiting on #{io.inspect}"
55
+ else
56
+ set[io] = Task.current
57
+ end
58
+
59
+ Task.suspend
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,5 @@
1
+ module Celluloid
2
+ module IO
3
+ VERSION = "0.7.0"
4
+ end
5
+ end
@@ -0,0 +1,43 @@
1
+ module Celluloid
2
+ module IO
3
+ class DeadWakerError < StandardError; end # You can't wake the dead
4
+
5
+ # Wakes up sleepy threads so that they can check their mailbox
6
+ # Works like a ConditionVariable, except it's implemented as an IO object so
7
+ # that it can be multiplexed alongside other IO objects.
8
+ class Waker
9
+ PAYLOAD = "\0" # the payload doesn't matter. each byte is a signal
10
+ def initialize
11
+ @receiver, @sender = ::IO.pipe
12
+ end
13
+
14
+ # Wakes up the thread that is waiting for this Waker
15
+ def signal
16
+ @sender << PAYLOAD
17
+ nil
18
+ rescue IOError, Errno::EPIPE, Errno::EBADF
19
+ raise DeadWakerError, "waker is already dead"
20
+ end
21
+
22
+ # Wait for another thread to signal this Waker
23
+ def wait
24
+ byte = @receiver.read(1)
25
+ raise DeadWakerError, "can't wait on a dead waker" unless byte == PAYLOAD
26
+ rescue IOError, RuntimeError
27
+ raise DeadWakerError, "can't wait on a dead waker"
28
+ end
29
+
30
+ # Return the IO object which will be readable when this Waker is signaled
31
+ def io
32
+ @receiver
33
+ end
34
+
35
+ # Clean up the IO objects associated with this waker
36
+ def cleanup
37
+ @receiver.close rescue nil
38
+ @sender.close rescue nil
39
+ nil
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::IO do
4
+ it_behaves_like "a Celluloid Actor", Celluloid::IO
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::IO::Mailbox do
4
+ it_behaves_like "a Celluloid Mailbox"
5
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::IO::Waker do
4
+ it "blocks until awoken" do
5
+ waker = Celluloid::IO::Waker.new
6
+ thread = Thread.new do
7
+ waker.wait
8
+ :done
9
+ end
10
+
11
+ # Assert that the thread can't be joined at this point
12
+ thread.join(0).should be_nil
13
+
14
+ waker.signal
15
+ thread.value.should == :done
16
+ end
17
+
18
+ it "returns an IO object that can be multiplexed with IO.select" do
19
+ waker = Celluloid::IO::Waker.new
20
+ waker.io.should be_an_instance_of(IO)
21
+
22
+ thread = Thread.new do
23
+ readable, _, _ = select [waker.io]
24
+ waker.wait
25
+ :done
26
+ end
27
+
28
+ # Assert that the thread can't be joined at this point
29
+ thread.join(0).should be_nil
30
+
31
+ waker.signal
32
+ thread.value.should == :done
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'celluloid/io'
4
+ require 'celluloid/rspec'
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: celluloid-io
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tony Arcieri
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: celluloid
16
+ requirement: &70176481988080 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.7.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70176481988080
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70176481987440 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70176481987440
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70176481986860 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 2.7.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70176481986860
47
+ description: Evented IO for Celluloid actors
48
+ email:
49
+ - tony.arcieri@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - .rspec
56
+ - .travis.yml
57
+ - CHANGES.md
58
+ - Gemfile
59
+ - LICENSE.txt
60
+ - README.md
61
+ - Rakefile
62
+ - celluloid-io.gemspec
63
+ - lib/celluloid/io.rb
64
+ - lib/celluloid/io/mailbox.rb
65
+ - lib/celluloid/io/reactor.rb
66
+ - lib/celluloid/io/version.rb
67
+ - lib/celluloid/io/waker.rb
68
+ - spec/celluloid/io/actor_spec.rb
69
+ - spec/celluloid/io/mailbox_spec.rb
70
+ - spec/celluloid/io/waker_spec.rb
71
+ - spec/spec_helper.rb
72
+ homepage: http://github.com/tarcieri/celluloid-io
73
+ licenses: []
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 1.8.10
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Celluloid::IO allows you to monitor multiple IO objects within a Celluloid
96
+ actor
97
+ test_files:
98
+ - spec/celluloid/io/actor_spec.rb
99
+ - spec/celluloid/io/mailbox_spec.rb
100
+ - spec/celluloid/io/waker_spec.rb
101
+ - spec/spec_helper.rb