celluloid-io 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +1 -1
- data/CHANGES.md +5 -0
- data/README.md +32 -30
- data/celluloid-io.gemspec +4 -3
- data/lib/celluloid/io.rb +4 -10
- data/lib/celluloid/io/mailbox.rb +23 -26
- data/lib/celluloid/io/reactor.rb +27 -35
- data/lib/celluloid/io/version.rb +1 -1
- metadata +23 -14
- data/lib/celluloid/io/waker.rb +0 -43
- data/spec/celluloid/io/waker_spec.rb +0 -34
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -35,36 +35,38 @@ Usage
|
|
35
35
|
|
36
36
|
To use Celluloid::IO, define a normal Ruby class that includes Celluloid::IO:
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
38
|
+
```ruby
|
39
|
+
require 'celluloid/io'
|
40
|
+
|
41
|
+
class MyServer
|
42
|
+
include Celluloid::IO
|
43
|
+
|
44
|
+
# Bind a TCP server to the given host and port
|
45
|
+
def initialize(host, port)
|
46
|
+
@server = TCPServer.new host, port
|
47
|
+
run!
|
48
|
+
end
|
49
|
+
|
50
|
+
# Run the TCP server event loop
|
51
|
+
def run
|
52
|
+
while true
|
53
|
+
wait_readable(@server)
|
54
|
+
on_connect @server.accept
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Terminate this server
|
59
|
+
def terminate
|
60
|
+
@server.close
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
# Called whenever a new connection is opened
|
65
|
+
def on_connect(connection)
|
66
|
+
connection.close
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
68
70
|
|
69
71
|
Contributing to Celluloid::IO
|
70
72
|
-----------------------------
|
data/celluloid-io.gemspec
CHANGED
@@ -15,8 +15,9 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Celluloid::IO::VERSION
|
17
17
|
|
18
|
-
gem.
|
18
|
+
gem.add_dependency 'celluloid', '~> 0.8.0'
|
19
|
+
gem.add_dependency 'nio4r', '>= 0.2.2'
|
19
20
|
|
20
|
-
gem.add_development_dependency
|
21
|
-
gem.add_development_dependency
|
21
|
+
gem.add_development_dependency 'rake'
|
22
|
+
gem.add_development_dependency 'rspec', '~> 2.7.0'
|
22
23
|
end
|
data/lib/celluloid/io.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'celluloid/io/version'
|
2
2
|
|
3
|
+
require 'forwardable'
|
3
4
|
require 'celluloid'
|
4
5
|
require 'celluloid/io/mailbox'
|
5
6
|
require 'celluloid/io/reactor'
|
6
|
-
require 'celluloid/io/waker'
|
7
7
|
|
8
8
|
module Celluloid
|
9
9
|
# Actors with evented IO support
|
@@ -13,15 +13,9 @@ module Celluloid
|
|
13
13
|
klass.use_mailbox Celluloid::IO::Mailbox
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
def wait_readable(io, &block)
|
18
|
-
# Law of demeter be damned!
|
19
|
-
current_actor.mailbox.reactor.wait_readable(io, &block)
|
20
|
-
end
|
16
|
+
extend Forwardable
|
21
17
|
|
22
|
-
# Wait for the given IO object to become writeable
|
23
|
-
|
24
|
-
current_actor.mailbox.reactor.wait_writeable(io, &block)
|
25
|
-
end
|
18
|
+
# Wait for the given IO object to become readable/writeable
|
19
|
+
def_delegators 'current_actor.mailbox.reactor', :wait_readable, :wait_writeable
|
26
20
|
end
|
27
21
|
end
|
data/lib/celluloid/io/mailbox.rb
CHANGED
@@ -1,24 +1,23 @@
|
|
1
1
|
module Celluloid
|
2
2
|
module IO
|
3
|
-
# An alternative implementation of Celluloid::Mailbox using
|
3
|
+
# An alternative implementation of Celluloid::Mailbox using Reactor
|
4
4
|
class Mailbox < Celluloid::Mailbox
|
5
|
-
attr_reader :reactor
|
5
|
+
attr_reader :reactor
|
6
6
|
|
7
7
|
def initialize
|
8
8
|
@messages = []
|
9
9
|
@lock = Mutex.new
|
10
|
-
@
|
11
|
-
@reactor = Reactor.new(@waker)
|
10
|
+
@reactor = Reactor.new
|
12
11
|
end
|
13
12
|
|
14
13
|
# Add a message to the Mailbox
|
15
14
|
def <<(message)
|
16
15
|
@lock.synchronize do
|
17
16
|
@messages << message
|
18
|
-
@
|
17
|
+
@reactor.wakeup
|
19
18
|
end
|
20
19
|
nil
|
21
|
-
rescue
|
20
|
+
rescue IOError
|
22
21
|
raise MailboxError, "dead recipient"
|
23
22
|
end
|
24
23
|
|
@@ -26,10 +25,10 @@ module Celluloid
|
|
26
25
|
def system_event(event)
|
27
26
|
@lock.synchronize do
|
28
27
|
@messages.unshift event
|
29
|
-
|
28
|
+
|
30
29
|
begin
|
31
|
-
@
|
32
|
-
rescue
|
30
|
+
@reactor.wakeup
|
31
|
+
rescue IOError
|
33
32
|
# Silently fail if messages are sent to dead actors
|
34
33
|
end
|
35
34
|
end
|
@@ -38,35 +37,33 @@ module Celluloid
|
|
38
37
|
|
39
38
|
# Receive a message from the Mailbox
|
40
39
|
def receive(timeout = nil, &block)
|
41
|
-
message =
|
40
|
+
message = next_message(&block)
|
42
41
|
|
43
|
-
|
42
|
+
until message
|
44
43
|
if timeout
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
@reactor.run_once(wait_interval) do
|
54
|
-
@waker.wait
|
55
|
-
message = next_message(&block)
|
44
|
+
now = Time.now
|
45
|
+
wait_until ||= now + timeout
|
46
|
+
wait_interval = wait_until - now
|
47
|
+
return if wait_interval < 0
|
48
|
+
else
|
49
|
+
wait_interval = nil
|
56
50
|
end
|
57
|
-
|
51
|
+
|
52
|
+
@reactor.run_once(wait_interval)
|
53
|
+
message = next_message(&block)
|
54
|
+
end
|
58
55
|
|
59
56
|
message
|
60
|
-
rescue IOError
|
57
|
+
rescue IOError
|
61
58
|
shutdown # force shutdown of the mailbox
|
62
59
|
raise MailboxShutdown, "mailbox shutdown called during receive"
|
63
60
|
end
|
64
61
|
|
65
62
|
# Cleanup any IO objects this Mailbox may be using
|
66
63
|
def shutdown
|
67
|
-
@
|
64
|
+
@reactor.shutdown
|
68
65
|
super
|
69
66
|
end
|
70
67
|
end
|
71
68
|
end
|
72
|
-
end
|
69
|
+
end
|
data/lib/celluloid/io/reactor.rb
CHANGED
@@ -1,45 +1,33 @@
|
|
1
|
+
require 'nio'
|
2
|
+
|
1
3
|
module Celluloid
|
2
4
|
module IO
|
3
5
|
# React to external I/O events. This is kinda sorta supposed to resemble the
|
4
6
|
# Reactor design pattern.
|
5
7
|
class Reactor
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
# Unblock the reactor (i.e. to signal it from another thread)
|
11
|
+
def_delegator :@selector, :wakeup
|
12
|
+
# Terminate the reactor
|
13
|
+
def_delegator :@selector, :close, :shutdown
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@selector = NIO::Selector.new
|
10
17
|
end
|
11
18
|
|
12
19
|
# Wait for the given IO object to become readable
|
13
20
|
def wait_readable(io)
|
14
|
-
|
21
|
+
wait io, :r
|
15
22
|
end
|
16
23
|
|
17
24
|
# Wait for the given IO object to become writeable
|
18
25
|
def wait_writeable(io)
|
19
|
-
|
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
|
26
|
+
wait io, :w
|
36
27
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
#######
|
41
|
-
|
42
|
-
def wait_for_io(io, set)
|
28
|
+
|
29
|
+
# Wait for the given IO operation to complete
|
30
|
+
def wait(io, set)
|
43
31
|
# zomg ugly type conversion :(
|
44
32
|
unless io.is_a?(IO)
|
45
33
|
if IO.respond_to? :try_convert
|
@@ -49,14 +37,18 @@ module Celluloid
|
|
49
37
|
else raise TypeError, "can't convert #{io.class} into IO"
|
50
38
|
end
|
51
39
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
40
|
+
|
41
|
+
monitor = @selector.register(io, set)
|
42
|
+
monitor.value = Task.current
|
43
|
+
Task.suspend :iowait
|
44
|
+
end
|
45
|
+
|
46
|
+
# Run the reactor, waiting for events or wakeup signal
|
47
|
+
def run_once(timeout = nil)
|
48
|
+
@selector.select_each(timeout) do |monitor|
|
49
|
+
monitor.value.resume
|
50
|
+
@selector.deregister(monitor.io)
|
57
51
|
end
|
58
|
-
|
59
|
-
Task.suspend
|
60
52
|
end
|
61
53
|
end
|
62
54
|
end
|
data/lib/celluloid/io/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: celluloid-io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,22 +9,33 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-01-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: celluloid
|
16
|
-
requirement: &
|
16
|
+
requirement: &70254645083180 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.8.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70254645083180
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: nio4r
|
27
|
+
requirement: &70254645082660 !ruby/object:Gem::Requirement
|
17
28
|
none: false
|
18
29
|
requirements:
|
19
30
|
- - ! '>='
|
20
31
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.
|
22
|
-
type: :
|
32
|
+
version: 0.2.2
|
33
|
+
type: :runtime
|
23
34
|
prerelease: false
|
24
|
-
version_requirements: *
|
35
|
+
version_requirements: *70254645082660
|
25
36
|
- !ruby/object:Gem::Dependency
|
26
37
|
name: rake
|
27
|
-
requirement: &
|
38
|
+
requirement: &70254645082180 !ruby/object:Gem::Requirement
|
28
39
|
none: false
|
29
40
|
requirements:
|
30
41
|
- - ! '>='
|
@@ -32,18 +43,18 @@ dependencies:
|
|
32
43
|
version: '0'
|
33
44
|
type: :development
|
34
45
|
prerelease: false
|
35
|
-
version_requirements: *
|
46
|
+
version_requirements: *70254645082180
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
48
|
name: rspec
|
38
|
-
requirement: &
|
49
|
+
requirement: &70254645081480 !ruby/object:Gem::Requirement
|
39
50
|
none: false
|
40
51
|
requirements:
|
41
|
-
- -
|
52
|
+
- - ~>
|
42
53
|
- !ruby/object:Gem::Version
|
43
54
|
version: 2.7.0
|
44
55
|
type: :development
|
45
56
|
prerelease: false
|
46
|
-
version_requirements: *
|
57
|
+
version_requirements: *70254645081480
|
47
58
|
description: Evented IO for Celluloid actors
|
48
59
|
email:
|
49
60
|
- tony.arcieri@gmail.com
|
@@ -64,10 +75,8 @@ files:
|
|
64
75
|
- lib/celluloid/io/mailbox.rb
|
65
76
|
- lib/celluloid/io/reactor.rb
|
66
77
|
- lib/celluloid/io/version.rb
|
67
|
-
- lib/celluloid/io/waker.rb
|
68
78
|
- spec/celluloid/io/actor_spec.rb
|
69
79
|
- spec/celluloid/io/mailbox_spec.rb
|
70
|
-
- spec/celluloid/io/waker_spec.rb
|
71
80
|
- spec/spec_helper.rb
|
72
81
|
homepage: http://github.com/tarcieri/celluloid-io
|
73
82
|
licenses: []
|
@@ -97,5 +106,5 @@ summary: Celluloid::IO allows you to monitor multiple IO objects within a Cellul
|
|
97
106
|
test_files:
|
98
107
|
- spec/celluloid/io/actor_spec.rb
|
99
108
|
- spec/celluloid/io/mailbox_spec.rb
|
100
|
-
- spec/celluloid/io/waker_spec.rb
|
101
109
|
- spec/spec_helper.rb
|
110
|
+
has_rdoc:
|
data/lib/celluloid/io/waker.rb
DELETED
@@ -1,43 +0,0 @@
|
|
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
|
@@ -1,34 +0,0 @@
|
|
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
|