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,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::StackDump do
4
+ class BlockingActor
5
+ include Celluloid
6
+
7
+ def blocking
8
+ Kernel.sleep
9
+ end
10
+ end
11
+
12
+ before(:each) do
13
+ [Celluloid::TaskFiber, Celluloid::TaskThread].each do |task_klass|
14
+ actor_klass = Class.new(BlockingActor) do
15
+ task_class task_klass
16
+ end
17
+ actor = actor_klass.new
18
+ actor.async.blocking
19
+ end
20
+
21
+ Celluloid.internal_pool.get do
22
+ Thread.current.role = :testing
23
+ sleep
24
+ end
25
+ end
26
+
27
+ it 'should include all actors' do
28
+ subject.actors.size.should == Celluloid::Actor.all.size
29
+ end
30
+
31
+ it 'should include threads that are not actors' do
32
+ pending "bugs"
33
+ subject.threads.size.should == 2
34
+ end
35
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::SupervisionGroup do
4
+ before :all do
5
+ class MyActor
6
+ include Celluloid
7
+
8
+ def running?; :yep; end
9
+ end
10
+
11
+ class MyGroup < Celluloid::SupervisionGroup
12
+ supervise MyActor, :as => :example
13
+ end
14
+ end
15
+
16
+ it "runs applications" do
17
+ MyGroup.run!
18
+ sleep 0.01 # startup time hax
19
+
20
+ Celluloid::Actor[:example].should be_running
21
+ end
22
+
23
+ it "accepts a private actor registry" do
24
+ my_registry = Celluloid::Registry.new
25
+ MyGroup.run!(my_registry)
26
+ sleep 0.01
27
+
28
+ my_registry[:example].should be_running
29
+ end
30
+
31
+ context "pool" do
32
+ before :all do
33
+ class MyActor
34
+ attr_reader :args
35
+ def initialize *args
36
+ @args = *args
37
+ end
38
+ end
39
+ class MyGroup
40
+ pool MyActor, :as => :example_pool, :args => 'foo', :size => 3
41
+ end
42
+ end
43
+
44
+ it "runs applications and passes pool options and actor args" do
45
+ MyGroup.run!
46
+ sleep 0.001 # startup time hax
47
+
48
+ Celluloid::Actor[:example_pool].should be_running
49
+ Celluloid::Actor[:example_pool].args.should eq ['foo']
50
+ Celluloid::Actor[:example_pool].size.should be 3
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::Supervisor do
4
+ class SubordinateDead < StandardError; end
5
+
6
+ class Subordinate
7
+ include Celluloid
8
+ attr_reader :state
9
+
10
+ def initialize(state)
11
+ @state = state
12
+ end
13
+
14
+ def crack_the_whip
15
+ case @state
16
+ when :idle
17
+ @state = :working
18
+ else raise SubordinateDead, "the spec purposely crashed me :("
19
+ end
20
+ end
21
+ end
22
+
23
+ it "restarts actors when they die" do
24
+ supervisor = Celluloid::Supervisor.supervise(Subordinate, :idle)
25
+ subordinate = supervisor.actors.first
26
+ subordinate.state.should be(:idle)
27
+
28
+ subordinate.crack_the_whip
29
+ subordinate.state.should be(:working)
30
+
31
+ expect do
32
+ subordinate.crack_the_whip
33
+ end.to raise_exception(SubordinateDead)
34
+ sleep 0.1 # hax to prevent race :(
35
+ subordinate.should_not be_alive
36
+
37
+ new_subordinate = supervisor.actors.first
38
+ new_subordinate.should_not eq subordinate
39
+ new_subordinate.state.should eq :idle
40
+ end
41
+
42
+ it "registers actors and reregisters them when they die" do
43
+ Celluloid::Supervisor.supervise_as(:subordinate, Subordinate, :idle)
44
+ subordinate = Celluloid::Actor[:subordinate]
45
+ subordinate.state.should be(:idle)
46
+
47
+ subordinate.crack_the_whip
48
+ subordinate.state.should be(:working)
49
+
50
+ expect do
51
+ subordinate.crack_the_whip
52
+ end.to raise_exception(SubordinateDead)
53
+ sleep 0.1 # hax to prevent race :(
54
+ subordinate.should_not be_alive
55
+
56
+ new_subordinate = Celluloid::Actor[:subordinate]
57
+ new_subordinate.should_not eq subordinate
58
+ new_subordinate.state.should eq :idle
59
+ end
60
+
61
+ it "creates supervisors via Actor.supervise" do
62
+ supervisor = Subordinate.supervise(:working)
63
+ subordinate = supervisor.actors.first
64
+ subordinate.state.should be(:working)
65
+
66
+ expect do
67
+ subordinate.crack_the_whip
68
+ end.to raise_exception(SubordinateDead)
69
+ sleep 0.1 # hax to prevent race :(
70
+ subordinate.should_not be_alive
71
+
72
+ new_subordinate = supervisor.actors.first
73
+ new_subordinate.should_not eq subordinate
74
+ new_subordinate.state.should eq :working
75
+ end
76
+
77
+ it "creates supervisors and registers actors via Actor.supervise_as" do
78
+ supervisor = Subordinate.supervise_as(:subordinate, :working)
79
+ subordinate = Celluloid::Actor[:subordinate]
80
+ subordinate.state.should be(:working)
81
+
82
+ expect do
83
+ subordinate.crack_the_whip
84
+ end.to raise_exception(SubordinateDead)
85
+ sleep 0.1 # hax to prevent race :(
86
+ subordinate.should_not be_alive
87
+
88
+ new_subordinate = supervisor.actors.first
89
+ new_subordinate.should_not eq subordinate
90
+ new_subordinate.state.should be(:working)
91
+ end
92
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::TaskFiber do
4
+ it_behaves_like "a Celluloid Task", Celluloid::TaskFiber
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::TaskThread do
4
+ it_behaves_like "a Celluloid Task", Celluloid::TaskThread
5
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::ThreadHandle do
4
+ it "knows thread liveliness" do
5
+ queue = Queue.new
6
+ handle = Celluloid::ThreadHandle.new { queue.pop }
7
+ handle.should be_alive
8
+
9
+ queue << :die
10
+
11
+ sleep 0.01 # hax
12
+ handle.should_not be_alive
13
+ end
14
+
15
+ it "joins to thread handles" do
16
+ Celluloid::ThreadHandle.new { sleep 0.01 }.join
17
+ end
18
+
19
+ it "supports passing a role" do
20
+ Celluloid::ThreadHandle.new(:useful) { Thread.current.role.should == :useful }.join
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::UUID do
4
+ U = Celluloid::UUID
5
+
6
+ it "generates unique IDs across the BLOCK_SIZE boundary" do
7
+ upper_bound = U::BLOCK_SIZE * 2 + 10
8
+ uuids = (1..upper_bound).map{ U.generate }
9
+ uuids.size.should == uuids.uniq.size
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'rubygems'
5
+ require 'bundler/setup'
6
+ require 'celluloid'
7
+ require 'celluloid/rspec'
8
+
9
+ logfile = File.open(File.expand_path("../../log/test.log", __FILE__), 'a')
10
+ logfile.sync = true
11
+
12
+ logger = Celluloid.logger = Logger.new(logfile)
13
+
14
+ Celluloid.shutdown_timeout = 1
15
+
16
+ Dir['./spec/support/*.rb'].map {|f| require f }
17
+
18
+ RSpec.configure do |config|
19
+ config.filter_run :focus => true
20
+ config.run_all_when_everything_filtered = true
21
+
22
+ config.before do
23
+ Celluloid.logger = logger
24
+ Celluloid.shutdown
25
+ sleep 0.01
26
+
27
+ Celluloid.internal_pool.assert_inactive
28
+
29
+ Celluloid.boot
30
+ end
31
+ end
@@ -34,6 +34,11 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
34
34
  actor.object_id.should_not eq(Kernel.object_id)
35
35
  end
36
36
 
37
+ it "implements respond_to? correctly" do
38
+ actor = actor_class.new 'Troy McClure'
39
+ actor.should respond_to(:alive?)
40
+ end
41
+
37
42
  it "supports synchronous calls" do
38
43
  actor = actor_class.new "Troy McClure"
39
44
  actor.greet.should eq("Hi, I'm Troy McClure")
@@ -91,6 +96,36 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
91
96
  ponycopter.greet_by_proxy(actor).should eq("Hi, I'm a ponycopter!")
92
97
  end
93
98
 
99
+ it "detects recursion" do
100
+ klass1 = Class.new do
101
+ include included_module
102
+ task_class task_klass
103
+
104
+ def recursion_test(recurse_through = nil)
105
+ if recurse_through
106
+ recurse_through.recursion_thunk(Celluloid::Actor.current)
107
+ else
108
+ Celluloid.detect_recursion
109
+ end
110
+ end
111
+ end
112
+
113
+ klass2 = Class.new do
114
+ include included_module
115
+ task_class task_klass
116
+
117
+ def recursion_thunk(other)
118
+ other.recursion_test
119
+ end
120
+ end
121
+
122
+ actor1 = klass1.new
123
+ actor2 = klass2.new
124
+
125
+ actor1.recursion_test.should be_false
126
+ actor1.recursion_test(actor2).should be_true
127
+ end
128
+
94
129
  it "properly handles method_missing" do
95
130
  actor = actor_class.new "Method Missing"
96
131
  actor.should respond_to(:first)
@@ -103,6 +138,24 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
103
138
  actor.respond_to?(:zomg_private, true).should be_true
104
139
  end
105
140
 
141
+ it "warns about suspending the initialize" do
142
+ klass = Class.new do
143
+ include included_module
144
+ task_class task_klass
145
+
146
+ def initialize
147
+ sleep 0.1
148
+ end
149
+ end
150
+
151
+ Celluloid.logger = double.as_null_object
152
+ Celluloid.logger.should_receive(:warn).with(/Dangerously suspending task: type=:call, meta={:method_name=>:initialize}, status=:sleeping/)
153
+
154
+ actor = klass.new
155
+ actor.terminate
156
+ Celluloid::Actor.join(actor) unless defined?(JRUBY_VERSION)
157
+ end
158
+
106
159
  it "calls the user defined finalizer" do
107
160
  actor = actor_class.new "Mr. Bean"
108
161
  actor.wrapped_object.should_receive(:my_finalizer)
@@ -110,6 +163,26 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
110
163
  Celluloid::Actor.join(actor)
111
164
  end
112
165
 
166
+ it "warns about suspending the finalizer" do
167
+ klass = Class.new do
168
+ include included_module
169
+ task_class task_klass
170
+
171
+ finalizer :cleanup
172
+
173
+ def cleanup
174
+ sleep 0.1
175
+ end
176
+ end
177
+
178
+ Celluloid.logger = double.as_null_object
179
+ Celluloid.logger.should_receive(:warn).with(/Dangerously suspending task: type=:finalizer, meta={:method_name=>:cleanup}, status=:sleeping/)
180
+
181
+ actor = klass.new
182
+ actor.terminate
183
+ Celluloid::Actor.join(actor)
184
+ end
185
+
113
186
  it "supports async(:method) syntax for asynchronous calls" do
114
187
  actor = actor_class.new "Troy McClure"
115
188
  actor.async :change_name, "Charlie Sheen"
@@ -162,6 +235,27 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
162
235
  actor.inspect.should include('dead')
163
236
  end
164
237
 
238
+ it "supports recursive inspect with other actors" do
239
+ klass = Class.new do
240
+ include included_module
241
+ task_class task_klass
242
+
243
+ attr_accessor :other
244
+
245
+ def initialize(other = nil)
246
+ @other = other
247
+ end
248
+ end
249
+
250
+ itchy = klass.new
251
+ scratchy = klass.new(itchy)
252
+ itchy.other = scratchy
253
+
254
+ inspection = itchy.inspect
255
+ inspection.should match(/Celluloid::ActorProxy\(/)
256
+ inspection.should include("...")
257
+ end
258
+
165
259
  it "allows access to the wrapped object" do
166
260
  actor = actor_class.new "Troy McClure"
167
261
  actor.wrapped_object.should be_a actor_class
@@ -239,6 +333,8 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
239
333
  actor = actor_class.new "James Dean"
240
334
  actor.crash rescue nil
241
335
 
336
+ sleep 0.1 # hax to prevent a race between exit handling and the next call
337
+
242
338
  expect do
243
339
  actor.greet
244
340
  end.to raise_exception(Celluloid::DeadActorError)
@@ -283,6 +379,14 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
283
379
  actor.should_not be_alive
284
380
  end
285
381
 
382
+ it "can be terminated by a SyncCall" do
383
+ actor = actor_class.new "Arnold Schwarzenegger"
384
+ actor.should be_alive
385
+ actor.shutdown
386
+ Celluloid::Actor.join(actor)
387
+ actor.should_not be_alive
388
+ end
389
+
286
390
  it "kills" do # THOU SHALT ALWAYS KILL
287
391
  actor = actor_class.new "Woody Harrelson"
288
392
  actor.should be_alive
@@ -308,6 +412,17 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
308
412
  actor.terminate!
309
413
  end.to raise_exception(Celluloid::DeadActorError, "actor already terminated")
310
414
  end
415
+
416
+ it "logs a warning when terminating tasks" do
417
+ Celluloid.logger = double.as_null_object
418
+ Celluloid.logger.should_receive(:warn).with("Terminating task: type=:call, meta={:method_name=>:sleepy}, status=:sleeping")
419
+
420
+ actor = actor_class.new "Arnold Schwarzenegger"
421
+ actor.async.sleepy 10
422
+ actor.greet # make sure the actor has started sleeping
423
+
424
+ actor.terminate
425
+ end
311
426
  end
312
427
 
313
428
  context :current_actor do
@@ -438,8 +553,6 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
438
553
  raise "already signaled" if @signaled
439
554
 
440
555
  @waiting = true
441
- signal :future
442
-
443
556
  value = wait :ponycopter
444
557
 
445
558
  @waiting = false
@@ -447,11 +560,6 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
447
560
  value
448
561
  end
449
562
 
450
- def wait_for_future
451
- return true if @waiting
452
- wait :future
453
- end
454
-
455
563
  def send_signal(value)
456
564
  signal :ponycopter, value
457
565
  end
@@ -467,9 +575,11 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
467
575
 
468
576
  obj.async.wait_for_signal
469
577
  obj.should_not be_signaled
578
+ obj.should be_waiting
470
579
 
471
580
  obj.send_signal :foobar
472
581
  obj.should be_signaled
582
+ obj.should_not be_waiting
473
583
  end
474
584
 
475
585
  it "sends values along with signals" do
@@ -478,7 +588,6 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
478
588
 
479
589
  future = obj.future(:wait_for_signal)
480
590
 
481
- obj.wait_for_future
482
591
  obj.should be_waiting
483
592
  obj.should_not be_signaled
484
593
 
@@ -799,12 +908,12 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
799
908
  end
800
909
  end
801
910
 
802
- context :mailbox_limit do
911
+ context :mailbox_size do
803
912
  subject do
804
913
  Class.new do
805
914
  include included_module
806
915
  task_class task_klass
807
- mailbox.max_size = 100
916
+ mailbox_size 100
808
917
  end
809
918
  end
810
919
 
@@ -857,4 +966,46 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
857
966
  subclass.new.tasks.first.should be_a ExampleTask
858
967
  end
859
968
  end
969
+
970
+ context :timeouts do
971
+ let :actor_class do
972
+ Class.new do
973
+ include included_module
974
+
975
+ def name
976
+ sleep 0.5
977
+ :foo
978
+ end
979
+
980
+ def ask_name_with_timeout(other, duration)
981
+ timeout(duration) { other.name }
982
+ end
983
+ end
984
+ end
985
+
986
+ it "allows timing out tasks, raising Celluloid::Task::TimeoutError" do
987
+ a1 = actor_class.new
988
+ a2 = actor_class.new
989
+
990
+ expect { a1.ask_name_with_timeout a2, 0.3 }.to raise_error(Celluloid::Task::TimeoutError)
991
+ end
992
+
993
+ it "does not raise when it completes in time" do
994
+ a1 = actor_class.new
995
+ a2 = actor_class.new
996
+
997
+ a1.ask_name_with_timeout(a2, 0.6).should == :foo
998
+ end
999
+ end
1000
+
1001
+ context "raw message sends" do
1002
+ it "logs on unhandled messages" do
1003
+ Celluloid.logger = double.as_null_object
1004
+ Celluloid.logger.should_receive(:debug).with("Discarded message (unhandled): first")
1005
+
1006
+ actor = actor_class.new "Irma Gladden"
1007
+ actor.mailbox << :first
1008
+ sleep Celluloid::TIMER_QUANTUM
1009
+ end
1010
+ end
860
1011
  end