celluloid 0.6.2 → 0.7.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/README.md +37 -482
- data/lib/celluloid.rb +45 -18
- data/lib/celluloid/actor.rb +64 -22
- data/lib/celluloid/actor_pool.rb +1 -1
- data/lib/celluloid/actor_proxy.rb +1 -1
- data/lib/celluloid/application.rb +1 -1
- data/lib/celluloid/calls.rb +2 -2
- data/lib/celluloid/fiber.rb +2 -31
- data/lib/celluloid/fsm.rb +141 -0
- data/lib/celluloid/future.rb +10 -13
- data/lib/celluloid/logger.rb +8 -3
- data/lib/celluloid/mailbox.rb +16 -4
- data/lib/celluloid/receivers.rb +49 -19
- data/lib/celluloid/registry.rb +2 -2
- data/lib/celluloid/signals.rb +12 -10
- data/lib/celluloid/supervisor.rb +7 -4
- data/lib/celluloid/task.rb +53 -0
- data/lib/celluloid/tcp_server.rb +2 -1
- data/lib/celluloid/timers.rb +109 -0
- data/lib/celluloid/version.rb +1 -1
- data/spec/support/actor_examples.rb +453 -0
- data/spec/support/mailbox_examples.rb +52 -0
- metadata +11 -11
- data/lib/celluloid/io.rb +0 -24
- data/lib/celluloid/io/mailbox.rb +0 -65
- data/lib/celluloid/io/reactor.rb +0 -63
- data/lib/celluloid/io/waker.rb +0 -43
@@ -0,0 +1,52 @@
|
|
1
|
+
shared_context "a Celluloid Mailbox" do
|
2
|
+
class TestEvent < Celluloid::SystemEvent; end
|
3
|
+
|
4
|
+
it "receives messages" do
|
5
|
+
message = :ohai
|
6
|
+
|
7
|
+
subject << message
|
8
|
+
subject.receive.should == message
|
9
|
+
end
|
10
|
+
|
11
|
+
it "raises system events when received" do
|
12
|
+
subject.system_event TestEvent.new("example")
|
13
|
+
|
14
|
+
expect do
|
15
|
+
subject.receive
|
16
|
+
end.to raise_exception(TestEvent)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "prioritizes system events over other messages" do
|
20
|
+
subject << :dummy1
|
21
|
+
subject << :dummy2
|
22
|
+
subject.system_event TestEvent.new("example")
|
23
|
+
|
24
|
+
expect do
|
25
|
+
subject.receive
|
26
|
+
end.to raise_exception(TestEvent)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "selectively receives messages with a block" do
|
30
|
+
class Foo; end
|
31
|
+
class Bar; end
|
32
|
+
class Baz; end
|
33
|
+
|
34
|
+
foo, bar, baz = Foo.new, Bar.new, Baz.new
|
35
|
+
|
36
|
+
subject << baz
|
37
|
+
subject << foo
|
38
|
+
subject << bar
|
39
|
+
|
40
|
+
subject.receive { |msg| msg.is_a? Foo }.should == foo
|
41
|
+
subject.receive { |msg| msg.is_a? Bar }.should == bar
|
42
|
+
subject.receive.should == baz
|
43
|
+
end
|
44
|
+
|
45
|
+
it "waits for a given timeout interval" do
|
46
|
+
interval = 0.1
|
47
|
+
started_at = Time.now
|
48
|
+
|
49
|
+
subject.receive(interval) { false }
|
50
|
+
(Time.now - started_at).should be_within(Celluloid::Timer::QUANTUM).of interval
|
51
|
+
end
|
52
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: celluloid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-12-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &70176959879340 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70176959879340
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70176959878380 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: 2.7.0
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70176959878380
|
36
36
|
description: Celluloid is a concurrent object framework inspired by the Actor Model
|
37
37
|
email:
|
38
38
|
- tony@medioh.com
|
@@ -49,11 +49,8 @@ files:
|
|
49
49
|
- lib/celluloid/core_ext.rb
|
50
50
|
- lib/celluloid/events.rb
|
51
51
|
- lib/celluloid/fiber.rb
|
52
|
+
- lib/celluloid/fsm.rb
|
52
53
|
- lib/celluloid/future.rb
|
53
|
-
- lib/celluloid/io/mailbox.rb
|
54
|
-
- lib/celluloid/io/reactor.rb
|
55
|
-
- lib/celluloid/io/waker.rb
|
56
|
-
- lib/celluloid/io.rb
|
57
54
|
- lib/celluloid/links.rb
|
58
55
|
- lib/celluloid/logger.rb
|
59
56
|
- lib/celluloid/mailbox.rb
|
@@ -63,9 +60,13 @@ files:
|
|
63
60
|
- lib/celluloid/rspec.rb
|
64
61
|
- lib/celluloid/signals.rb
|
65
62
|
- lib/celluloid/supervisor.rb
|
63
|
+
- lib/celluloid/task.rb
|
66
64
|
- lib/celluloid/tcp_server.rb
|
65
|
+
- lib/celluloid/timers.rb
|
67
66
|
- lib/celluloid/version.rb
|
68
67
|
- lib/celluloid.rb
|
68
|
+
- spec/support/actor_examples.rb
|
69
|
+
- spec/support/mailbox_examples.rb
|
69
70
|
homepage: https://github.com/tarcieri/celluloid
|
70
71
|
licenses:
|
71
72
|
- MIT
|
@@ -92,4 +93,3 @@ signing_key:
|
|
92
93
|
specification_version: 3
|
93
94
|
summary: Celluloid is a concurrent object framework inspired by the Actor Model
|
94
95
|
test_files: []
|
95
|
-
has_rdoc:
|
data/lib/celluloid/io.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'celluloid/io/waker'
|
2
|
-
require 'celluloid/io/reactor'
|
3
|
-
require 'celluloid/io/mailbox'
|
4
|
-
|
5
|
-
module Celluloid
|
6
|
-
# Actors which can run alongside other I/O operations
|
7
|
-
module IO
|
8
|
-
def self.included(klass)
|
9
|
-
klass.send :include, ::Celluloid
|
10
|
-
klass.use_mailbox Celluloid::IO::Mailbox
|
11
|
-
end
|
12
|
-
|
13
|
-
# Wait for the given IO object to become readable
|
14
|
-
def wait_readable(io, &block)
|
15
|
-
# Law of demeter be damned!
|
16
|
-
current_actor.mailbox.reactor.wait_readable(io, &block)
|
17
|
-
end
|
18
|
-
|
19
|
-
# Wait for the given IO object to become writeable
|
20
|
-
def wait_writeable(io, &block)
|
21
|
-
current_actor.mailbox.reactor.wait_writeable(io, &block)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
data/lib/celluloid/io/mailbox.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
module Celluloid
|
4
|
-
module IO
|
5
|
-
# An alternative implementation of Celluloid::Mailbox using Wakers
|
6
|
-
class Mailbox < Celluloid::Mailbox
|
7
|
-
attr_reader :reactor, :waker
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@messages = []
|
11
|
-
@lock = Mutex.new
|
12
|
-
@waker = Waker.new
|
13
|
-
@reactor = Reactor.new(@waker)
|
14
|
-
end
|
15
|
-
|
16
|
-
# Add a message to the Mailbox
|
17
|
-
def <<(message)
|
18
|
-
@lock.synchronize do
|
19
|
-
@messages << message
|
20
|
-
@waker.signal
|
21
|
-
end
|
22
|
-
nil
|
23
|
-
rescue DeadWakerError
|
24
|
-
raise MailboxError, "dead recipient"
|
25
|
-
end
|
26
|
-
|
27
|
-
# Add a high-priority system event to the Mailbox
|
28
|
-
def system_event(event)
|
29
|
-
@lock.synchronize do
|
30
|
-
@messages.unshift event
|
31
|
-
|
32
|
-
begin
|
33
|
-
@waker.signal
|
34
|
-
rescue DeadWakerError
|
35
|
-
# Silently fail if messages are sent to dead actors
|
36
|
-
end
|
37
|
-
end
|
38
|
-
nil
|
39
|
-
end
|
40
|
-
|
41
|
-
# Receive a message from the Mailbox
|
42
|
-
def receive(&block)
|
43
|
-
message = nil
|
44
|
-
|
45
|
-
begin
|
46
|
-
@reactor.run_once do
|
47
|
-
@waker.wait
|
48
|
-
message = next_message(&block)
|
49
|
-
end
|
50
|
-
end until message
|
51
|
-
|
52
|
-
message
|
53
|
-
rescue IOError, DeadWakerError
|
54
|
-
shutdown # force shutdown of the mailbox
|
55
|
-
raise MailboxShutdown, "mailbox shutdown called during receive"
|
56
|
-
end
|
57
|
-
|
58
|
-
# Cleanup any IO objects this Mailbox may be using
|
59
|
-
def shutdown
|
60
|
-
@waker.cleanup
|
61
|
-
super
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
data/lib/celluloid/io/reactor.rb
DELETED
@@ -1,63 +0,0 @@
|
|
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
|
-
monitor_io io, @readers
|
15
|
-
Fiber.yield
|
16
|
-
block_given? ? yield(io) : io
|
17
|
-
end
|
18
|
-
|
19
|
-
# Wait for the given IO object to become writeable
|
20
|
-
def wait_writeable(io)
|
21
|
-
monitor_io io, @writers
|
22
|
-
Fiber.yield
|
23
|
-
block_given? ? yield(io) : io
|
24
|
-
end
|
25
|
-
|
26
|
-
# Run the reactor, waiting for events, and calling the given block if
|
27
|
-
# the reactor is awoken by the waker
|
28
|
-
def run_once
|
29
|
-
readers, writers = select @readers.keys << @waker.io, @writers.keys
|
30
|
-
yield if readers.include? @waker.io
|
31
|
-
|
32
|
-
[[readers, @readers], [writers, @writers]].each do |ios, registered|
|
33
|
-
ios.each do |io|
|
34
|
-
fiber = registered.delete io
|
35
|
-
fiber.resume if fiber
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
#######
|
41
|
-
private
|
42
|
-
#######
|
43
|
-
|
44
|
-
def monitor_io(io, set)
|
45
|
-
# zomg ugly type conversion :(
|
46
|
-
unless io.is_a?(IO)
|
47
|
-
if IO.respond_to? :try_convert
|
48
|
-
io = IO.try_convert(io)
|
49
|
-
elsif io.respond_to? :to_io
|
50
|
-
io = io.to_io
|
51
|
-
else raise TypeError, "can't convert #{io.class} into IO"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
if set.has_key? io
|
56
|
-
raise ArgumentError, "another method is already waiting on #{io.inspect}"
|
57
|
-
else
|
58
|
-
set[io] = Fiber.current
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
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
|