celluloid 0.15.2 → 0.16.0.pre3

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +20 -0
  3. data/README.md +25 -2
  4. data/lib/celluloid/actor.rb +88 -147
  5. data/lib/celluloid/actor_system.rb +107 -0
  6. data/lib/celluloid/call_chain.rb +1 -1
  7. data/lib/celluloid/calls.rb +16 -16
  8. data/lib/celluloid/cell.rb +89 -0
  9. data/lib/celluloid/condition.rb +25 -8
  10. data/lib/celluloid/cpu_counter.rb +27 -18
  11. data/lib/celluloid/evented_mailbox.rb +10 -16
  12. data/lib/celluloid/exceptions.rb +23 -0
  13. data/lib/celluloid/future.rb +1 -1
  14. data/lib/celluloid/handlers.rb +41 -0
  15. data/lib/celluloid/internal_pool.rb +49 -40
  16. data/lib/celluloid/logger.rb +30 -0
  17. data/lib/celluloid/logging/incident_logger.rb +1 -1
  18. data/lib/celluloid/mailbox.rb +35 -31
  19. data/lib/celluloid/method.rb +8 -0
  20. data/lib/celluloid/pool_manager.rb +19 -2
  21. data/lib/celluloid/probe.rb +73 -0
  22. data/lib/celluloid/properties.rb +2 -2
  23. data/lib/celluloid/proxies/actor_proxy.rb +9 -41
  24. data/lib/celluloid/proxies/cell_proxy.rb +68 -0
  25. data/lib/celluloid/proxies/sync_proxy.rb +1 -1
  26. data/lib/celluloid/receivers.rb +5 -13
  27. data/lib/celluloid/registry.rb +1 -8
  28. data/lib/celluloid/stack_dump.rb +34 -11
  29. data/lib/celluloid/supervision_group.rb +26 -14
  30. data/lib/celluloid/tasks/task_fiber.rb +6 -0
  31. data/lib/celluloid/tasks/task_thread.rb +2 -1
  32. data/lib/celluloid/tasks.rb +28 -9
  33. data/lib/celluloid/thread_handle.rb +2 -2
  34. data/lib/celluloid.rb +68 -73
  35. data/spec/celluloid/actor_spec.rb +1 -1
  36. data/spec/celluloid/actor_system_spec.rb +69 -0
  37. data/spec/celluloid/block_spec.rb +1 -1
  38. data/spec/celluloid/calls_spec.rb +1 -1
  39. data/spec/celluloid/condition_spec.rb +14 -3
  40. data/spec/celluloid/cpu_counter_spec.rb +82 -0
  41. data/spec/celluloid/fsm_spec.rb +1 -1
  42. data/spec/celluloid/future_spec.rb +1 -1
  43. data/spec/celluloid/notifications_spec.rb +1 -1
  44. data/spec/celluloid/pool_spec.rb +34 -1
  45. data/spec/celluloid/probe_spec.rb +121 -0
  46. data/spec/celluloid/registry_spec.rb +6 -6
  47. data/spec/celluloid/stack_dump_spec.rb +37 -8
  48. data/spec/celluloid/supervision_group_spec.rb +7 -1
  49. data/spec/celluloid/supervisor_spec.rb +12 -1
  50. data/spec/celluloid/tasks/task_fiber_spec.rb +1 -1
  51. data/spec/celluloid/tasks/task_thread_spec.rb +1 -1
  52. data/spec/celluloid/thread_handle_spec.rb +7 -3
  53. data/spec/celluloid/timer_spec.rb +48 -0
  54. data/spec/spec_helper.rb +20 -7
  55. data/spec/support/actor_examples.rb +58 -15
  56. data/spec/support/mailbox_examples.rb +9 -3
  57. data/spec/support/task_examples.rb +2 -0
  58. metadata +48 -22
data/lib/celluloid.rb CHANGED
@@ -3,25 +3,32 @@ require 'thread'
3
3
  require 'timeout'
4
4
  require 'set'
5
5
 
6
- if defined?(JRUBY_VERSION) && JRUBY_VERSION == "1.7.3"
7
- raise "Celluloid is broken on JRuby 1.7.3. Please upgrade to 1.7.4+"
8
- end
9
-
10
6
  module Celluloid
11
- VERSION = '0.15.2'
12
- Error = Class.new StandardError
7
+ # Expose all instance methods as singleton methods
8
+ extend self
9
+
10
+ VERSION = '0.16.0.pre3'
13
11
 
14
- extend self # expose all instance methods as singleton methods
12
+ # Linking times out after 5 seconds
13
+ LINKING_TIMEOUT = 5
15
14
 
16
15
  # Warning message added to Celluloid objects accessed outside their actors
17
16
  BARE_OBJECT_WARNING_MESSAGE = "WARNING: BARE CELLULOID OBJECT "
18
17
 
19
18
  class << self
20
- attr_accessor :internal_pool # Internal thread pool
19
+ attr_writer :actor_system # Default Actor System
21
20
  attr_accessor :logger # Thread-safe logger class
22
21
  attr_accessor :task_class # Default task type to use
23
22
  attr_accessor :shutdown_timeout # How long actors have to terminate
24
23
 
24
+ def actor_system
25
+ if Thread.current.celluloid?
26
+ Thread.current[:celluloid_actor_system] or raise Error, "actor system not running"
27
+ else
28
+ Thread.current[:celluloid_actor_system] || @actor_system or raise Error, "Celluloid is not yet started; use Celluloid.boot"
29
+ end
30
+ end
31
+
25
32
  def included(klass)
26
33
  klass.send :extend, ClassMethods
27
34
  klass.send :include, InstanceMethods
@@ -29,19 +36,29 @@ module Celluloid
29
36
  klass.send :extend, Properties
30
37
 
31
38
  klass.property :mailbox_class, :default => Celluloid::Mailbox
32
- klass.property :proxy_class, :default => Celluloid::ActorProxy
39
+ klass.property :proxy_class, :default => Celluloid::CellProxy
33
40
  klass.property :task_class, :default => Celluloid.task_class
34
41
  klass.property :mailbox_size
35
42
 
43
+ klass.property :exclusive_actor, :default => false
44
+ klass.property :exclusive_methods, :multi => true
36
45
  klass.property :execute_block_on_receiver,
37
46
  :default => [:after, :every, :receive],
38
47
  :multi => true
39
48
 
40
49
  klass.property :finalizer
41
- klass.property :exit_handler
50
+ klass.property :exit_handler_name
42
51
 
43
52
  klass.send(:define_singleton_method, :trap_exit) do |*args|
44
- exit_handler(*args)
53
+ exit_handler_name(*args)
54
+ end
55
+
56
+ klass.send(:define_singleton_method, :exclusive) do |*args|
57
+ if args.any?
58
+ exclusive_methods(*exclusive_methods, *args)
59
+ else
60
+ exclusive_actor true
61
+ end
45
62
  end
46
63
  end
47
64
 
@@ -69,7 +86,7 @@ module Celluloid
69
86
 
70
87
  # Perform a stack dump of all actors to the given output object
71
88
  def stack_dump(output = STDERR)
72
- Celluloid::StackDump.new.dump(output)
89
+ actor_system.stack_dump.print(output)
73
90
  end
74
91
  alias_method :dump, :stack_dump
75
92
 
@@ -106,18 +123,15 @@ module Celluloid
106
123
  end
107
124
 
108
125
  def init
109
- self.internal_pool = InternalPool.new
126
+ @actor_system = ActorSystem.new
110
127
  end
111
128
 
112
- # Launch default services
113
- # FIXME: We should set up the supervision hierarchy here
114
129
  def start
115
- Celluloid::Notifications::Fanout.supervise_as :notifications_fanout
116
- Celluloid::IncidentReporter.supervise_as :default_incident_reporter, STDERR
130
+ actor_system.start
117
131
  end
118
132
 
119
133
  def running?
120
- internal_pool
134
+ actor_system && actor_system.running?
121
135
  end
122
136
 
123
137
  def register_shutdown
@@ -139,41 +153,7 @@ module Celluloid
139
153
 
140
154
  # Shut down all running actors
141
155
  def shutdown
142
- actors = Actor.all
143
-
144
- Timeout.timeout(shutdown_timeout) do
145
- internal_pool.shutdown
146
-
147
- Logger.debug "Terminating #{actors.size} #{(actors.size > 1) ? 'actors' : 'actor'}..." if actors.size > 0
148
-
149
- # Attempt to shut down the supervision tree, if available
150
- Supervisor.root.terminate if Supervisor.root
151
-
152
- # Actors cannot self-terminate, you must do it for them
153
- actors.each do |actor|
154
- begin
155
- actor.terminate!
156
- rescue DeadActorError
157
- end
158
- end
159
-
160
- actors.each do |actor|
161
- begin
162
- Actor.join(actor)
163
- rescue DeadActorError
164
- end
165
- end
166
- end
167
- rescue Timeout::Error
168
- Logger.error("Couldn't cleanly terminate all actors in #{shutdown_timeout} seconds!")
169
- actors.each do |actor|
170
- begin
171
- Actor.kill(actor)
172
- rescue DeadActorError, MailboxDead
173
- end
174
- end
175
- ensure
176
- internal_pool.kill
156
+ actor_system.shutdown
177
157
  end
178
158
 
179
159
  def version
@@ -185,7 +165,7 @@ module Celluloid
185
165
  module ClassMethods
186
166
  # Create a new actor
187
167
  def new(*args, &block)
188
- proxy = Actor.new(allocate, actor_options).proxy
168
+ proxy = Cell.new(allocate, behavior_options, actor_options).proxy
189
169
  proxy._send_(:initialize, *args, &block)
190
170
  proxy
191
171
  end
@@ -195,7 +175,7 @@ module Celluloid
195
175
  def new_link(*args, &block)
196
176
  raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
197
177
 
198
- proxy = Actor.new(allocate, actor_options).proxy
178
+ proxy = Cell.new(allocate, behavior_options, actor_options).proxy
199
179
  Actor.link(proxy)
200
180
  proxy._send_(:initialize, *args, &block)
201
181
  proxy
@@ -233,26 +213,28 @@ module Celluloid
233
213
  Actor.join(new(*args, &block))
234
214
  end
235
215
 
236
- # Mark methods as running exclusively
237
- def exclusive(*methods)
238
- if methods.empty?
239
- @exclusive_methods = :all
240
- elsif !defined?(@exclusive_methods) || @exclusive_methods != :all
241
- @exclusive_methods ||= Set.new
242
- @exclusive_methods.merge methods.map(&:to_sym)
243
- end
216
+ def actor_system
217
+ Celluloid.actor_system
244
218
  end
245
219
 
246
220
  # Configuration options for Actor#new
247
221
  def actor_options
248
222
  {
223
+ :actor_system => actor_system,
249
224
  :mailbox_class => mailbox_class,
250
225
  :mailbox_size => mailbox_size,
251
- :proxy_class => proxy_class,
252
226
  :task_class => task_class,
253
- :exit_handler => exit_handler,
254
- :exclusive_methods => defined?(@exclusive_methods) ? @exclusive_methods : nil,
255
- :receiver_block_executions => execute_block_on_receiver
227
+ :exclusive => exclusive_actor,
228
+ }
229
+ end
230
+
231
+ def behavior_options
232
+ {
233
+ :proxy_class => proxy_class,
234
+ :exclusive_methods => exclusive_methods,
235
+ :exit_handler_name => exit_handler_name,
236
+ :finalizer => finalizer,
237
+ :receiver_block_executions => execute_block_on_receiver,
256
238
  }
257
239
  end
258
240
 
@@ -291,9 +273,10 @@ module Celluloid
291
273
  end
292
274
 
293
275
  # Obtain the name of the current actor
294
- def name
295
- Actor.name
276
+ def registered_name
277
+ Actor.registered_name
296
278
  end
279
+ alias_method :name, :registered_name
297
280
 
298
281
  def inspect
299
282
  return "..." if Celluloid.detect_recursion
@@ -303,7 +286,7 @@ module Celluloid
303
286
  if leaked?
304
287
  str << Celluloid::BARE_OBJECT_WARNING_MESSAGE
305
288
  else
306
- str << "Celluloid::ActorProxy"
289
+ str << "Celluloid::CellProxy"
307
290
  end
308
291
 
309
292
  str << "(#{self.class}:0x#{object_id.to_s(16)})"
@@ -335,7 +318,7 @@ module Celluloid
335
318
 
336
319
  # Terminate this actor
337
320
  def terminate
338
- Thread.current[:celluloid_actor].proxy.terminate!
321
+ Thread.current[:celluloid_actor].behavior_proxy.terminate!
339
322
  end
340
323
 
341
324
  # Send a signal with the given name to all waiting methods
@@ -458,15 +441,21 @@ module Celluloid
458
441
 
459
442
  # Handle async calls within an actor itself
460
443
  def async(meth = nil, *args, &block)
461
- Thread.current[:celluloid_actor].proxy.async meth, *args, &block
444
+ Thread.current[:celluloid_actor].behavior_proxy.async meth, *args, &block
462
445
  end
463
446
 
464
447
  # Handle calls to future within an actor itself
465
448
  def future(meth = nil, *args, &block)
466
- Thread.current[:celluloid_actor].proxy.future meth, *args, &block
449
+ Thread.current[:celluloid_actor].behavior_proxy.future meth, *args, &block
467
450
  end
468
451
  end
469
452
 
453
+ if defined?(JRUBY_VERSION) && JRUBY_VERSION == "1.7.3"
454
+ raise "Celluloid is broken on JRuby 1.7.3. Please upgrade to 1.7.4+"
455
+ end
456
+
457
+ require 'celluloid/exceptions'
458
+
470
459
  require 'celluloid/calls'
471
460
  require 'celluloid/call_chain'
472
461
  require 'celluloid/condition'
@@ -482,6 +471,7 @@ require 'celluloid/mailbox'
482
471
  require 'celluloid/evented_mailbox'
483
472
  require 'celluloid/method'
484
473
  require 'celluloid/properties'
474
+ require 'celluloid/handlers'
485
475
  require 'celluloid/receivers'
486
476
  require 'celluloid/registry'
487
477
  require 'celluloid/responses'
@@ -495,13 +485,16 @@ require 'celluloid/uuid'
495
485
 
496
486
  require 'celluloid/proxies/abstract_proxy'
497
487
  require 'celluloid/proxies/sync_proxy'
488
+ require 'celluloid/proxies/cell_proxy'
498
489
  require 'celluloid/proxies/actor_proxy'
499
490
  require 'celluloid/proxies/async_proxy'
500
491
  require 'celluloid/proxies/future_proxy'
501
492
  require 'celluloid/proxies/block_proxy'
502
493
 
503
494
  require 'celluloid/actor'
495
+ require 'celluloid/cell'
504
496
  require 'celluloid/future'
497
+ require 'celluloid/actor_system'
505
498
  require 'celluloid/pool_manager'
506
499
  require 'celluloid/supervision_group'
507
500
  require 'celluloid/supervisor'
@@ -510,6 +503,8 @@ require 'celluloid/logging'
510
503
 
511
504
  require 'celluloid/legacy' unless defined?(CELLULOID_FUTURE)
512
505
 
506
+ $CELLULOID_MONITORING = false
507
+
513
508
  # Configure default systemwide settings
514
509
  Celluloid.task_class = Celluloid::TaskFiber
515
510
  Celluloid.logger = Logger.new(STDERR)
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Celluloid do
3
+ describe Celluloid, actor_system: :global do
4
4
  it_behaves_like "a Celluloid Actor", Celluloid
5
5
  end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::ActorSystem do
4
+ class TestActor
5
+ include Celluloid
6
+ end
7
+
8
+ it "supports non-global ActorSystem" do
9
+ subject.within do
10
+ Celluloid.actor_system.should == subject
11
+ end
12
+ end
13
+
14
+ it "starts default actors" do
15
+ subject.start
16
+
17
+ subject.registered.should == [:notifications_fanout, :default_incident_reporter]
18
+ end
19
+
20
+ it "support getting threads" do
21
+ queue = Queue.new
22
+ thread = subject.get_thread do
23
+ Celluloid.actor_system.should == subject
24
+ queue << nil
25
+ end
26
+ queue.pop
27
+ end
28
+
29
+ it "allows a stack dump" do
30
+ subject.stack_dump.should be_a(Celluloid::StackDump)
31
+ end
32
+
33
+ it "returns named actors" do
34
+ subject.registered.should be_empty
35
+
36
+ subject.within do
37
+ TestActor.supervise_as :test
38
+ end
39
+
40
+ subject.registered.should == [:test]
41
+ end
42
+
43
+ it "returns running actors" do
44
+ subject.running.should be_empty
45
+
46
+ first = subject.within do
47
+ TestActor.new
48
+ end
49
+
50
+ second = subject.within do
51
+ TestActor.new
52
+ end
53
+
54
+ subject.running.should == [first, second]
55
+ end
56
+
57
+ it "shuts down" do
58
+ subject.shutdown
59
+
60
+ lambda { subject.get_thread }.
61
+ should raise_error("Thread pool is not running")
62
+ end
63
+
64
+ it "warns nicely when no actor system is started" do
65
+ lambda { TestActor.new }.
66
+ should raise_error("Celluloid is not yet started; use Celluloid.boot")
67
+ end
68
+
69
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Blocks" do
3
+ describe "Blocks", actor_system: :global do
4
4
  class MyBlockActor
5
5
  include Celluloid
6
6
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Celluloid::SyncCall do
3
+ describe Celluloid::SyncCall, actor_system: :global do
4
4
  class CallExampleActor
5
5
  include Celluloid
6
6
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Celluloid::Condition do
3
+ describe Celluloid::Condition, actor_system: :global do
4
4
  class ConditionExample
5
5
  include Celluloid
6
6
 
@@ -17,10 +17,10 @@ describe Celluloid::Condition do
17
17
  condition.signal value
18
18
  end
19
19
 
20
- def wait_for_condition
20
+ def wait_for_condition(timeout = nil)
21
21
  @waiting = true
22
22
  begin
23
- value = @condition.wait
23
+ value = @condition.wait(timeout)
24
24
  @signaled_times += 1
25
25
  ensure
26
26
  @waiting = false
@@ -62,4 +62,15 @@ describe Celluloid::Condition do
62
62
  actor.async.signal_condition condition, :value
63
63
  condition.wait.should eq(:value)
64
64
  end
65
+
66
+ it "times out inside normal Threads" do
67
+ condition = Celluloid::Condition.new
68
+ lambda { condition.wait(1) }.
69
+ should raise_error(Celluloid::ConditionError)
70
+ end
71
+
72
+ it "times out inside Tasks" do
73
+ lambda { actor.wait_for_condition(1) }.
74
+ should raise_error(Celluloid::ConditionError)
75
+ end
65
76
  end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe Celluloid::CPUCounter do
4
+ describe :cores do
5
+ subject { described_class.cores }
6
+
7
+ let(:num_cores) { 1024 }
8
+
9
+ before do
10
+ described_class.stub(:`) { fail 'backtick stub called' }
11
+ ::IO.stub(:open).and_raise('IO.open stub called!')
12
+ described_class.instance_variable_set('@cores', nil)
13
+ end
14
+
15
+ after { ENV['NUMBER_OF_PROCESSORS'] = nil }
16
+
17
+ context 'from valid env value' do
18
+ before { ENV['NUMBER_OF_PROCESSORS'] = num_cores.to_s }
19
+ it { should eq num_cores }
20
+ end
21
+
22
+ context 'from invalid env value' do
23
+ before { ENV['NUMBER_OF_PROCESSORS'] = '' }
24
+ specify { expect { subject }.to raise_error(ArgumentError) }
25
+ end
26
+
27
+ context 'with no env value' do
28
+ before { ENV['NUMBER_OF_PROCESSORS'] = nil }
29
+
30
+ context 'when /sys/devices/system/cpu/present exists' do
31
+ before do
32
+ ::IO.should_receive(:read).with('/sys/devices/system/cpu/present')
33
+ .and_return("dunno-whatever-#{num_cores - 1}")
34
+ end
35
+ it { should eq num_cores }
36
+ end
37
+
38
+ context 'when /sys/devices/system/cpu/present does NOT exist' do
39
+ before do
40
+ ::IO.should_receive(:read).with('/sys/devices/system/cpu/present')
41
+ .and_raise(Errno::ENOENT)
42
+ end
43
+
44
+ context 'when /sys/devices/system/cpu/cpu* files exist' do
45
+ before do
46
+ cpu_entries = (1..num_cores).map { |n| "cpu#{n}" }
47
+ cpu_entries << 'non-cpu-entry-to-ignore'
48
+ Dir.should_receive(:[]).with('/sys/devices/system/cpu/cpu*')
49
+ .and_return(cpu_entries)
50
+ end
51
+ it { should eq num_cores }
52
+ end
53
+
54
+ context 'when /sys/devices/system/cpu/cpu* files DO NOT exist' do
55
+ before do
56
+ Dir.should_receive(:[]).with('/sys/devices/system/cpu/cpu*')
57
+ .and_return([])
58
+ end
59
+
60
+ context 'when sysctl blows up' do
61
+ before { described_class.stub(:`).and_raise(Errno::EINTR) }
62
+ specify { expect { subject }.to raise_error }
63
+ end
64
+
65
+ context 'when sysctl fails' do
66
+ before { described_class.stub(:`).and_return(`false`) }
67
+ it { should be nil }
68
+ end
69
+
70
+ context 'when sysctl succeeds' do
71
+ before do
72
+ described_class.should_receive(:`).with('sysctl -n hw.ncpu')
73
+ .and_return(num_cores.to_s)
74
+ `true`
75
+ end
76
+ it { should eq num_cores }
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Celluloid::FSM do
3
+ describe Celluloid::FSM, actor_system: :global do
4
4
  before :all do
5
5
  class TestMachine
6
6
  include Celluloid::FSM
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Celluloid::Future do
3
+ describe Celluloid::Future, actor_system: :global do
4
4
  it "creates future objects that can be retrieved later" do
5
5
  future = Celluloid::Future.new { 40 + 2 }
6
6
  future.value.should == 42
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Celluloid::Notifications do
3
+ describe Celluloid::Notifications, actor_system: :global do
4
4
  class Admirer
5
5
  include Celluloid
6
6
  include Celluloid::Notifications
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Celluloid.pool" do
3
+ describe "Celluloid.pool", actor_system: :global do
4
4
  class ExampleError < StandardError; end
5
5
 
6
6
  class MyWorker
@@ -14,11 +14,23 @@ describe "Celluloid.pool" do
14
14
  end
15
15
  end
16
16
 
17
+ def sleepy_work
18
+ t = Time.now.to_f
19
+ sleep 0.25
20
+ t
21
+ end
22
+
17
23
  def crash
18
24
  raise ExampleError, "zomgcrash"
19
25
  end
20
26
  end
21
27
 
28
+ def test_concurrency_of(pool)
29
+ baseline = Time.now.to_f
30
+ values = 10.times.map { pool.future.sleepy_work }.map(&:value)
31
+ values.select {|t| t - baseline < 0.1 }.length
32
+ end
33
+
22
34
  subject { MyWorker.pool }
23
35
 
24
36
  it "processes work units synchronously" do
@@ -56,4 +68,25 @@ describe "Celluloid.pool" do
56
68
  end
57
69
  futures.map(&:value)
58
70
  end
71
+
72
+ context "#size=" do
73
+ subject { MyWorker.pool size: 4 }
74
+
75
+ it "should adjust the pool size up", pending: 'flaky' do
76
+ expect(test_concurrency_of(subject)).to eq(4)
77
+
78
+ subject.size = 6
79
+ subject.size.should == 6
80
+
81
+ test_concurrency_of(subject).should == 6
82
+ end
83
+
84
+ it "should adjust the pool size down" do
85
+ test_concurrency_of(subject).should == 4
86
+
87
+ subject.size = 2
88
+ subject.size.should == 2
89
+ test_concurrency_of(subject).should == 2
90
+ end
91
+ end
59
92
  end