celluloid 0.14.1 → 0.15.0.pre

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -2
  3. data/lib/celluloid.rb +92 -108
  4. data/lib/celluloid/actor.rb +42 -64
  5. data/lib/celluloid/autostart.rb +1 -1
  6. data/lib/celluloid/call_chain.rb +13 -0
  7. data/lib/celluloid/calls.rb +5 -8
  8. data/lib/celluloid/condition.rb +8 -10
  9. data/lib/celluloid/cpu_counter.rb +1 -1
  10. data/lib/celluloid/evented_mailbox.rb +7 -10
  11. data/lib/celluloid/fsm.rb +1 -1
  12. data/lib/celluloid/future.rb +1 -2
  13. data/lib/celluloid/internal_pool.rb +77 -20
  14. data/lib/celluloid/legacy.rb +0 -38
  15. data/lib/celluloid/mailbox.rb +17 -10
  16. data/lib/celluloid/pool_manager.rb +1 -1
  17. data/lib/celluloid/properties.rb +24 -0
  18. data/lib/celluloid/proxies/abstract_proxy.rb +3 -0
  19. data/lib/celluloid/proxies/actor_proxy.rb +3 -32
  20. data/lib/celluloid/proxies/async_proxy.rb +4 -8
  21. data/lib/celluloid/proxies/future_proxy.rb +8 -6
  22. data/lib/celluloid/proxies/sync_proxy.rb +12 -7
  23. data/lib/celluloid/rspec.rb +3 -1
  24. data/lib/celluloid/signals.rb +7 -35
  25. data/lib/celluloid/stack_dump.rb +50 -37
  26. data/lib/celluloid/supervision_group.rb +5 -5
  27. data/lib/celluloid/task_set.rb +49 -0
  28. data/lib/celluloid/tasks.rb +67 -42
  29. data/lib/celluloid/tasks/task_fiber.rb +3 -1
  30. data/lib/celluloid/tasks/task_thread.rb +2 -3
  31. data/lib/celluloid/thread.rb +2 -0
  32. data/spec/celluloid/actor_spec.rb +5 -0
  33. data/spec/celluloid/block_spec.rb +54 -0
  34. data/spec/celluloid/calls_spec.rb +42 -0
  35. data/spec/celluloid/condition_spec.rb +65 -0
  36. data/spec/celluloid/evented_mailbox_spec.rb +34 -0
  37. data/spec/celluloid/fsm_spec.rb +107 -0
  38. data/spec/celluloid/future_spec.rb +32 -0
  39. data/spec/celluloid/internal_pool_spec.rb +52 -0
  40. data/spec/celluloid/links_spec.rb +45 -0
  41. data/spec/celluloid/logging/ring_buffer_spec.rb +38 -0
  42. data/spec/celluloid/mailbox_spec.rb +5 -0
  43. data/spec/celluloid/notifications_spec.rb +120 -0
  44. data/spec/celluloid/pool_spec.rb +52 -0
  45. data/spec/celluloid/properties_spec.rb +42 -0
  46. data/spec/celluloid/registry_spec.rb +64 -0
  47. data/spec/celluloid/stack_dump_spec.rb +35 -0
  48. data/spec/celluloid/supervision_group_spec.rb +53 -0
  49. data/spec/celluloid/supervisor_spec.rb +92 -0
  50. data/spec/celluloid/tasks/task_fiber_spec.rb +5 -0
  51. data/spec/celluloid/tasks/task_thread_spec.rb +5 -0
  52. data/spec/celluloid/thread_handle_spec.rb +22 -0
  53. data/spec/celluloid/uuid_spec.rb +11 -0
  54. data/spec/spec_helper.rb +31 -0
  55. data/spec/support/actor_examples.rb +161 -10
  56. data/spec/support/example_actor_class.rb +8 -0
  57. data/spec/support/mailbox_examples.rb +15 -3
  58. data/spec/support/task_examples.rb +2 -2
  59. metadata +28 -3
  60. data/lib/celluloid/version.rb +0 -4
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::Future do
4
+ it "creates future objects that can be retrieved later" do
5
+ future = Celluloid::Future.new { 40 + 2 }
6
+ future.value.should == 42
7
+ end
8
+
9
+ it "passes arguments to future blocks" do
10
+ future = Celluloid::Future.new(40) { |n| n + 2 }
11
+ future.value.should == 42
12
+ end
13
+
14
+ it "reraises exceptions that occur when the value is retrieved" do
15
+ class ExampleError < StandardError; end
16
+
17
+ future = Celluloid::Future.new { raise ExampleError, "oh noes crash!" }
18
+ expect { future.value }.to raise_exception(ExampleError)
19
+ end
20
+
21
+ it "knows if it's got a value yet" do
22
+ future = Celluloid::Future.new { sleep Celluloid::TIMER_QUANTUM * 5 }
23
+ future.should_not be_ready
24
+ sleep Celluloid::TIMER_QUANTUM * 6
25
+ future.should be_ready
26
+ end
27
+
28
+ it "raises TimeoutError when the future times out" do
29
+ future = Celluloid::Future.new { sleep 2 }
30
+ expect { future.value(1) }.to raise_exception(Celluloid::TimeoutError)
31
+ end
32
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::InternalPool do
4
+ it "gets threads from the pool" do
5
+ subject.get { sleep 1 }.should be_a Thread
6
+ end
7
+
8
+ it "puts threads back into the pool" do
9
+ subject.idle_size.should be_zero
10
+ subject.busy_size.should be_zero
11
+
12
+ queue = Queue.new
13
+ subject.get { queue.pop }
14
+
15
+ subject.idle_size.should be_zero
16
+ subject.busy_size.should eq 1
17
+
18
+ queue << nil
19
+ sleep 0.01 # hax
20
+
21
+ subject.idle_size.should eq 1
22
+ subject.busy_size.should eq 0
23
+ end
24
+
25
+ it "cleans thread locals from old threads" do
26
+ thread = subject.get { Thread.current[:foo] = :bar }
27
+
28
+ sleep 0.01 #hax
29
+ thread[:foo].should be_nil
30
+ end
31
+
32
+ it "doesn't fail if a third-party thread is spawned" do
33
+ subject.idle_size.should be_zero
34
+ subject.busy_size.should be_zero
35
+
36
+ subject.get { ::Thread.new { sleep 0.5 } }.should be_a(Celluloid::Thread)
37
+
38
+ sleep 0.01 # hax
39
+
40
+ subject.idle_size.should eq 1
41
+ subject.busy_size.should eq 0
42
+ end
43
+
44
+ it "doesn't leak dead threads" do
45
+ subject.max_idle = 0 # Instruct the pool to immediately shut down the thread.
46
+ subject.get { true }.should be_a(Celluloid::Thread)
47
+
48
+ sleep 0.01 # hax
49
+
50
+ subject.to_a.should have(0).items
51
+ end
52
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::Links do
4
+ subject { Celluloid::Links.new }
5
+
6
+ let(:mailbox_mock) do
7
+ Class.new(Array) do
8
+ attr_reader :address
9
+ def initialize address
10
+ @address = address
11
+ end
12
+ end
13
+ end
14
+
15
+ let(:first_actor) do
16
+ Struct.new(:mailbox).new(mailbox_mock.new('foo123'))
17
+ end
18
+
19
+ let(:second_actor) do
20
+ Struct.new(:mailbox).new(mailbox_mock.new('bar456'))
21
+ end
22
+
23
+ it 'is Enumerable' do
24
+ subject.should be_an(Enumerable)
25
+ end
26
+
27
+ it 'adds actors by their mailbox address' do
28
+ subject.include?(first_actor).should be_false
29
+ subject << first_actor
30
+ subject.include?(first_actor).should be_true
31
+ end
32
+
33
+ it 'removes actors by their mailbox address' do
34
+ subject << first_actor
35
+ subject.include?(first_actor).should be_true
36
+ subject.delete first_actor
37
+ subject.include?(first_actor).should be_false
38
+ end
39
+
40
+ it 'iterates over all actors' do
41
+ subject << first_actor
42
+ subject << second_actor
43
+ subject.inject([]) { |all, a| all << a }.should == [first_actor, second_actor]
44
+ end
45
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::RingBuffer do
4
+ subject { Celluloid::RingBuffer.new(2) }
5
+
6
+ it { should be_empty }
7
+ it { should_not be_full }
8
+
9
+ it 'should push and shift' do
10
+ subject.push('foo')
11
+ subject.push('foo2')
12
+ subject.shift.should eq('foo')
13
+ subject.shift.should eq('foo2')
14
+ end
15
+
16
+ it 'should push past the end' do
17
+ subject.push('foo')
18
+ subject.push('foo2')
19
+ subject.push('foo3')
20
+ subject.should be_full
21
+ end
22
+
23
+ it 'should shift the most recent' do
24
+ (1..5).each { |i| subject.push(i) }
25
+ subject.shift.should be 4
26
+ subject.shift.should be 5
27
+ subject.shift.should be_nil
28
+ end
29
+
30
+ it 'should return nil when shifting empty' do
31
+ subject.should be_empty
32
+ subject.shift.should be_nil
33
+ end
34
+
35
+ it 'should be thread-safe' do
36
+ #TODO how to test?
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::Mailbox do
4
+ it_behaves_like "a Celluloid Mailbox"
5
+ end
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::Notifications do
4
+ class Admirer
5
+ include Celluloid
6
+ include Celluloid::Notifications
7
+
8
+ attr_reader :mourning
9
+ attr_reader :mourning_count
10
+
11
+ def someone_died(topic, name)
12
+ @mourning = name
13
+ @mourning_count ||= 0
14
+ @mourning_count += 1
15
+ end
16
+ end
17
+
18
+ class President
19
+ include Celluloid
20
+ include Celluloid::Notifications
21
+
22
+ def die
23
+ publish("death", "Mr. President")
24
+ end
25
+ end
26
+
27
+ it 'notifies relevant subscribers' do
28
+ marilyn = Admirer.new
29
+ jackie = Admirer.new
30
+
31
+ marilyn.subscribe("death", :someone_died)
32
+ jackie.subscribe("alive", :someone_died)
33
+
34
+ president = President.new
35
+
36
+ president.die
37
+ marilyn.mourning.should eq("Mr. President")
38
+ jackie.mourning.should_not eq("Mr. President")
39
+ end
40
+
41
+ it 'allows multiple subscriptions from the same actor' do
42
+ marilyn = Admirer.new
43
+
44
+ marilyn.subscribe("death", :someone_died)
45
+ marilyn.subscribe("death", :someone_died)
46
+
47
+ president = President.new
48
+
49
+ president.die
50
+ marilyn.mourning_count.should be(2)
51
+ end
52
+
53
+
54
+ it 'notifies subscribers' do
55
+ marilyn = Admirer.new
56
+ jackie = Admirer.new
57
+
58
+ marilyn.subscribe("death", :someone_died)
59
+ jackie.subscribe("death", :someone_died)
60
+
61
+ president = President.new
62
+
63
+ president.die
64
+ marilyn.mourning.should eq("Mr. President")
65
+ jackie.mourning.should eq("Mr. President")
66
+ end
67
+
68
+ it 'publishes even if there are no subscribers' do
69
+ president = President.new
70
+ president.die
71
+ end
72
+
73
+ it 'allows regex subscriptions' do
74
+ marilyn = Admirer.new
75
+
76
+ marilyn.subscribe(/(death|assassination)/, :someone_died)
77
+
78
+ president = President.new
79
+ president.die
80
+ marilyn.mourning.should eq("Mr. President")
81
+ end
82
+
83
+ it 'allows unsubscribing' do
84
+ marilyn = Admirer.new
85
+
86
+ subscription = marilyn.subscribe("death", :someone_died)
87
+ marilyn.unsubscribe(subscription)
88
+
89
+ president = President.new
90
+ president.die
91
+ marilyn.mourning.should be_nil
92
+ end
93
+
94
+ it 'prunes dead subscriptions' do
95
+ marilyn = Admirer.new
96
+ jackie = Admirer.new
97
+
98
+ marilyn.subscribe("death", :someone_died)
99
+ jackie.subscribe("death", :someone_died)
100
+
101
+ listeners = Celluloid::Notifications.notifier.listeners_for("death").size
102
+ marilyn.terminate
103
+ after_listeners = Celluloid::Notifications.notifier.listeners_for("death").size
104
+
105
+ after_listeners.should == listeners - 1
106
+ end
107
+
108
+ it 'prunes multiple subscriptions from a dead actor' do
109
+ marilyn = Admirer.new
110
+
111
+ marilyn.subscribe("death", :someone_died)
112
+ marilyn.subscribe("death", :someone_died)
113
+
114
+ listeners = Celluloid::Notifications.notifier.listeners_for("death").size
115
+ marilyn.terminate
116
+ after_listeners = Celluloid::Notifications.notifier.listeners_for("death").size
117
+
118
+ after_listeners.should eq(listeners - 2)
119
+ end
120
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Celluloid.pool" do
4
+ class ExampleError < StandardError; end
5
+
6
+ class MyWorker
7
+ include Celluloid
8
+
9
+ def process(queue = nil)
10
+ if queue
11
+ queue << :done
12
+ else
13
+ :done
14
+ end
15
+ end
16
+
17
+ def crash
18
+ raise ExampleError, "zomgcrash"
19
+ end
20
+ end
21
+
22
+ subject { MyWorker.pool }
23
+
24
+ it "processes work units synchronously" do
25
+ subject.process.should be :done
26
+ end
27
+
28
+ it "processes work units asynchronously" do
29
+ queue = Queue.new
30
+ subject.async.process(queue)
31
+ queue.pop.should be :done
32
+ end
33
+
34
+ it "handles crashes" do
35
+ expect { subject.crash }.to raise_error(ExampleError)
36
+ subject.process.should be :done
37
+ end
38
+
39
+ it "uses a fixed-sized number of threads" do
40
+ subject # eagerly evaluate the pool to spawn it
41
+
42
+ actors = Celluloid::Actor.all
43
+ 100.times.map { subject.future(:process) }.map(&:value)
44
+
45
+ new_actors = Celluloid::Actor.all - actors
46
+ new_actors.should eq []
47
+ end
48
+
49
+ it "terminates" do
50
+ expect { subject.terminate }.to_not raise_exception
51
+ end
52
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::Properties do
4
+ let(:default_value) { 42 }
5
+ let(:changed_value) { 43 }
6
+
7
+ let(:example_class) do
8
+ Class.new do
9
+ extend Celluloid::Properties
10
+ property :baz, :default => 42
11
+ end
12
+ end
13
+
14
+ let(:example_subclass) do
15
+ Class.new(example_class)
16
+ end
17
+
18
+ let(:example_subclass_subclass) do
19
+ Class.new(example_subclass)
20
+ end
21
+
22
+ it "adds properties to classes" do
23
+ example_class.baz.should eq default_value
24
+ example_class.baz changed_value
25
+ example_class.baz.should eq changed_value
26
+ end
27
+
28
+ it "allows properties to be inherited" do
29
+ example_subclass.baz.should eq default_value
30
+ example_subclass.baz changed_value
31
+ example_subclass.baz.should eq changed_value
32
+ example_class.baz.should eq default_value
33
+ end
34
+
35
+ it "allows properties to be deeply inherited" do
36
+ example_subclass_subclass.baz.should eq default_value
37
+ example_subclass_subclass.baz changed_value
38
+ example_subclass_subclass.baz.should eq changed_value
39
+ example_subclass.baz.should eq default_value
40
+ example_class.baz.should eq default_value
41
+ end
42
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::Registry do
4
+ class Marilyn
5
+ include Celluloid
6
+
7
+ def sing_for(person)
8
+ "o/~ Happy birthday, #{person}"
9
+ end
10
+ end
11
+
12
+ it "registers Actors" do
13
+ Celluloid::Actor[:marilyn] = Marilyn.new
14
+ Celluloid::Actor[:marilyn].sing_for("Mr. President").should == "o/~ Happy birthday, Mr. President"
15
+ end
16
+
17
+ it "refuses to register non-Actors" do
18
+ expect do
19
+ Celluloid::Actor[:impostor] = Object.new
20
+ end.to raise_error TypeError
21
+ end
22
+
23
+ it "lists all registered actors" do
24
+ Celluloid::Actor[:marilyn] = Marilyn.new
25
+ Celluloid::Actor.registered.should include :marilyn
26
+ end
27
+
28
+ it "knows its name once registered" do
29
+ Celluloid::Actor[:marilyn] = Marilyn.new
30
+ Celluloid::Actor[:marilyn].name.should == :marilyn
31
+ end
32
+
33
+ describe :delete do
34
+ before do
35
+ Celluloid::Actor[:marilyn] ||= Marilyn.new
36
+ end
37
+
38
+ it "removes reference to actors' name from the registry" do
39
+ Celluloid::Registry.root.delete(:marilyn)
40
+ Celluloid::Actor.registered.should_not include :marilyn
41
+ end
42
+
43
+ it "returns actor removed from the registry" do
44
+ rval = Celluloid::Registry.root.delete(:marilyn)
45
+ rval.should be_kind_of(Marilyn)
46
+ end
47
+ end
48
+
49
+ describe :clear do
50
+ it "should return a hash of registered actors and remove them from the registry" do
51
+ Celluloid::Actor[:marilyn] ||= Marilyn.new
52
+ rval = Celluloid::Actor.clear_registry
53
+ begin
54
+ rval.should be_kind_of(Hash)
55
+ rval.should have_key(:marilyn)
56
+ rval[:marilyn].wrapped_object.should be_instance_of(Marilyn)
57
+ Celluloid::Actor.registered.should be_empty
58
+ ensure
59
+ # Repopulate the registry once we're done
60
+ rval.each { |key, actor| Celluloid::Actor[key] = actor }
61
+ end
62
+ end
63
+ end
64
+ end