concurrent-ruby 0.5.0 → 0.6.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -77
  3. data/lib/concurrent.rb +17 -2
  4. data/lib/concurrent/actor.rb +17 -0
  5. data/lib/concurrent/actor_context.rb +31 -0
  6. data/lib/concurrent/actor_ref.rb +39 -0
  7. data/lib/concurrent/agent.rb +12 -3
  8. data/lib/concurrent/async.rb +290 -0
  9. data/lib/concurrent/atomic.rb +5 -9
  10. data/lib/concurrent/cached_thread_pool.rb +39 -137
  11. data/lib/concurrent/channel/blocking_ring_buffer.rb +60 -0
  12. data/lib/concurrent/channel/buffered_channel.rb +83 -0
  13. data/lib/concurrent/channel/channel.rb +11 -0
  14. data/lib/concurrent/channel/probe.rb +19 -0
  15. data/lib/concurrent/channel/ring_buffer.rb +54 -0
  16. data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
  17. data/lib/concurrent/channel/waitable_list.rb +38 -0
  18. data/lib/concurrent/configuration.rb +92 -0
  19. data/lib/concurrent/dataflow.rb +9 -3
  20. data/lib/concurrent/delay.rb +88 -0
  21. data/lib/concurrent/exchanger.rb +31 -0
  22. data/lib/concurrent/fixed_thread_pool.rb +28 -122
  23. data/lib/concurrent/future.rb +10 -5
  24. data/lib/concurrent/immediate_executor.rb +3 -2
  25. data/lib/concurrent/ivar.rb +2 -1
  26. data/lib/concurrent/java_cached_thread_pool.rb +45 -0
  27. data/lib/concurrent/java_fixed_thread_pool.rb +37 -0
  28. data/lib/concurrent/java_thread_pool_executor.rb +194 -0
  29. data/lib/concurrent/per_thread_executor.rb +23 -0
  30. data/lib/concurrent/postable.rb +2 -0
  31. data/lib/concurrent/processor_count.rb +125 -0
  32. data/lib/concurrent/promise.rb +42 -18
  33. data/lib/concurrent/ruby_cached_thread_pool.rb +37 -0
  34. data/lib/concurrent/ruby_fixed_thread_pool.rb +31 -0
  35. data/lib/concurrent/ruby_thread_pool_executor.rb +268 -0
  36. data/lib/concurrent/ruby_thread_pool_worker.rb +69 -0
  37. data/lib/concurrent/simple_actor_ref.rb +124 -0
  38. data/lib/concurrent/thread_local_var.rb +1 -1
  39. data/lib/concurrent/thread_pool_executor.rb +30 -0
  40. data/lib/concurrent/timer_task.rb +13 -10
  41. data/lib/concurrent/tvar.rb +212 -0
  42. data/lib/concurrent/utilities.rb +1 -0
  43. data/lib/concurrent/version.rb +1 -1
  44. data/spec/concurrent/actor_context_spec.rb +37 -0
  45. data/spec/concurrent/actor_ref_shared.rb +313 -0
  46. data/spec/concurrent/actor_spec.rb +9 -1
  47. data/spec/concurrent/agent_spec.rb +97 -96
  48. data/spec/concurrent/async_spec.rb +320 -0
  49. data/spec/concurrent/cached_thread_pool_shared.rb +137 -0
  50. data/spec/concurrent/channel/blocking_ring_buffer_spec.rb +149 -0
  51. data/spec/concurrent/channel/buffered_channel_spec.rb +151 -0
  52. data/spec/concurrent/channel/channel_spec.rb +37 -0
  53. data/spec/concurrent/channel/probe_spec.rb +49 -0
  54. data/spec/concurrent/channel/ring_buffer_spec.rb +126 -0
  55. data/spec/concurrent/channel/unbuffered_channel_spec.rb +132 -0
  56. data/spec/concurrent/configuration_spec.rb +134 -0
  57. data/spec/concurrent/dataflow_spec.rb +109 -27
  58. data/spec/concurrent/delay_spec.rb +77 -0
  59. data/spec/concurrent/exchanger_spec.rb +66 -0
  60. data/spec/concurrent/fixed_thread_pool_shared.rb +136 -0
  61. data/spec/concurrent/future_spec.rb +60 -51
  62. data/spec/concurrent/global_thread_pool_shared.rb +33 -0
  63. data/spec/concurrent/immediate_executor_spec.rb +4 -25
  64. data/spec/concurrent/ivar_spec.rb +36 -23
  65. data/spec/concurrent/java_cached_thread_pool_spec.rb +64 -0
  66. data/spec/concurrent/java_fixed_thread_pool_spec.rb +64 -0
  67. data/spec/concurrent/java_thread_pool_executor_spec.rb +71 -0
  68. data/spec/concurrent/obligation_shared.rb +32 -20
  69. data/spec/concurrent/{global_thread_pool_spec.rb → per_thread_executor_spec.rb} +9 -13
  70. data/spec/concurrent/processor_count_spec.rb +20 -0
  71. data/spec/concurrent/promise_spec.rb +29 -41
  72. data/spec/concurrent/ruby_cached_thread_pool_spec.rb +69 -0
  73. data/spec/concurrent/ruby_fixed_thread_pool_spec.rb +39 -0
  74. data/spec/concurrent/ruby_thread_pool_executor_spec.rb +183 -0
  75. data/spec/concurrent/simple_actor_ref_spec.rb +219 -0
  76. data/spec/concurrent/thread_pool_class_cast_spec.rb +40 -0
  77. data/spec/concurrent/thread_pool_executor_shared.rb +155 -0
  78. data/spec/concurrent/thread_pool_shared.rb +98 -36
  79. data/spec/concurrent/tvar_spec.rb +137 -0
  80. data/spec/spec_helper.rb +4 -0
  81. data/spec/support/functions.rb +4 -0
  82. metadata +85 -20
  83. data/lib/concurrent/cached_thread_pool/worker.rb +0 -91
  84. data/lib/concurrent/channel.rb +0 -63
  85. data/lib/concurrent/fixed_thread_pool/worker.rb +0 -54
  86. data/lib/concurrent/global_thread_pool.rb +0 -42
  87. data/spec/concurrent/cached_thread_pool_spec.rb +0 -101
  88. data/spec/concurrent/channel_spec.rb +0 -86
  89. data/spec/concurrent/fixed_thread_pool_spec.rb +0 -92
  90. data/spec/concurrent/uses_global_thread_pool_shared.rb +0 -64
@@ -1,3 +1,4 @@
1
+ require 'rbconfig'
1
2
  require 'thread'
2
3
 
3
4
  module Concurrent
@@ -1,3 +1,3 @@
1
1
  module Concurrent
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.0.pre.1'
3
3
  end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe ActorContext do
6
+
7
+ let(:described_class) do
8
+ Class.new do
9
+ include ActorContext
10
+ end
11
+ end
12
+
13
+ it 'protects #initialize' do
14
+ expect {
15
+ described_class.new
16
+ }.to raise_error(NoMethodError)
17
+ end
18
+
19
+ context 'callbacks' do
20
+
21
+ subject { described_class.send(:new) }
22
+
23
+ specify { subject.should respond_to :on_start }
24
+
25
+ specify { subject.should respond_to :on_reset }
26
+
27
+ specify { subject.should respond_to :on_shutdown }
28
+ end
29
+
30
+ context '#spawn' do
31
+
32
+ it 'returns an ActorRef' do
33
+ described_class.spawn.should be_a ActorRef
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,313 @@
1
+ require 'spec_helper'
2
+
3
+ def shared_actor_test_class
4
+ Class.new do
5
+ include Concurrent::ActorContext
6
+
7
+ attr_reader :argv
8
+
9
+ def initialize(*args)
10
+ @argv = args
11
+ end
12
+
13
+ def receive(*msg)
14
+ case msg.first
15
+ when :poison
16
+ raise StandardError
17
+ when :bullet
18
+ raise Exception
19
+ when :stop
20
+ shutdown
21
+ when :terminate
22
+ Thread.current.kill
23
+ when :sleep
24
+ sleep(msg.last)
25
+ when :check
26
+ msg[1].set(msg.last)
27
+ else
28
+ msg.first
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ share_examples_for :actor_ref do
35
+
36
+ it 'includes ActorRef' do
37
+ subject.should be_a Concurrent::ActorRef
38
+ end
39
+
40
+ context 'running and shutdown' do
41
+
42
+ specify { subject.should respond_to :shutdown }
43
+
44
+ specify { subject.should be_running }
45
+
46
+ specify { subject.should_not be_shutdown }
47
+
48
+ specify do
49
+ subject.shutdown
50
+ sleep(0.1)
51
+ subject.should be_shutdown
52
+ end
53
+
54
+ it 'defines a shutdown method on the actor(s)' do
55
+ subject << :foo
56
+ subject << :stop
57
+ sleep(0.1)
58
+ subject.should be_shutdown
59
+ end
60
+ end
61
+
62
+ context '#post' do
63
+
64
+ it 'raises an exception when the message is empty' do
65
+ expect {
66
+ subject.post
67
+ }.to raise_error(ArgumentError)
68
+ end
69
+
70
+ it 'returns an IVar' do
71
+ subject.post(:foo).should be_a Concurrent::IVar
72
+ end
73
+
74
+ it 'fulfills the IVar when message is processed' do
75
+ ivar = subject.post(:foo)
76
+ sleep(0.1)
77
+ ivar.should be_fulfilled
78
+ ivar.value.should eq :foo
79
+ end
80
+
81
+ it 'rejects the IVar when message processing fails' do
82
+ ivar = subject.post(:poison)
83
+ sleep(0.1)
84
+ ivar.should be_rejected
85
+ ivar.reason.should be_a StandardError
86
+ end
87
+ end
88
+
89
+ context '#<<' do
90
+
91
+ it 'posts the message' do
92
+ ivar = Concurrent::IVar.new
93
+ subject << [:check, ivar, :foo]
94
+ ivar.value(0.1).should eq :foo
95
+ end
96
+
97
+ it 'returns self' do
98
+ (subject << [1,2,3,4]).should eq subject
99
+ end
100
+ end
101
+
102
+ context '#post with callback' do
103
+
104
+ specify 'on success calls the callback with time and value' do
105
+ expected_value = expected_reason = nil
106
+ subject.post(:foo) do |time, value, reason|
107
+ expected_value = value
108
+ expected_reason = reason
109
+ end
110
+ sleep(0.1)
111
+
112
+ expected_value.should eq :foo
113
+ expected_reason.should be_nil
114
+ end
115
+
116
+ specify 'on failure calls the callback with time and reason' do
117
+ expected_value = expected_reason = nil
118
+ subject.post(:poison) do |time, value, reason|
119
+ expected_value = value
120
+ expected_reason = reason
121
+ end
122
+ sleep(0.1)
123
+
124
+ expected_value.should be_nil
125
+ expected_reason.should be_a StandardError
126
+ end
127
+
128
+ it 'supresses exceptions thrown by the callback' do
129
+ expected = nil
130
+ subject.post(:foo){|time, value, reason| raise StandardError }
131
+ sleep(0.1)
132
+
133
+ subject.post(:bar){|time, value, reason| expected = value }
134
+ sleep(0.1)
135
+
136
+ expected.should eq :bar
137
+ end
138
+ end
139
+
140
+ context '#post!' do
141
+
142
+ it 'raises an exception when the message is empty' do
143
+ expect {
144
+ subject.post!(1)
145
+ }.to raise_error(ArgumentError)
146
+ end
147
+
148
+ it 'blocks for up to the given number of seconds' do
149
+ start = Time.now.to_f
150
+ begin
151
+ subject.post!(1, :sleep, 2)
152
+ rescue
153
+ end
154
+ delta = Time.now.to_f - start
155
+ delta.should >= 1
156
+ delta.should <= 2
157
+ end
158
+
159
+ it 'blocks forever when the timeout is nil' do
160
+ start = Time.now.to_f
161
+ begin
162
+ subject.post!(nil, :sleep, 1)
163
+ rescue
164
+ end
165
+ delta = Time.now.to_f - start
166
+ delta.should > 1
167
+ end
168
+
169
+ it 'raises a TimeoutError when timeout is zero' do
170
+ expect {
171
+ subject.post!(0, :foo)
172
+ }.to raise_error(Concurrent::TimeoutError)
173
+ end
174
+
175
+ it 'raises a TimeoutError when the timeout is reached' do
176
+ expect {
177
+ subject.post!(1, :sleep, 10)
178
+ }.to raise_error(Concurrent::TimeoutError)
179
+ end
180
+
181
+ it 'returns the result of success processing' do
182
+ subject.post!(1, :foo).should eq :foo
183
+ end
184
+
185
+ it 'bubbles exceptions thrown during processing' do
186
+ expect {
187
+ subject.post!(1, :poison)
188
+ }.to raise_error(StandardError)
189
+ end
190
+ end
191
+
192
+ context '#join' do
193
+
194
+ it 'blocks until shutdown when no limit is given' do
195
+ start = Time.now
196
+ subject << :foo # start the actor's thread
197
+ Thread.new{ sleep(1); subject.shutdown }
198
+ subject.join
199
+ stop = Time.now
200
+
201
+ subject.should be_shutdown
202
+ stop.should >= start + 1
203
+ stop.should <= start + 2
204
+ end
205
+
206
+ it 'blocks for no more than the given number of seconds' do
207
+ start = Time.now
208
+ subject << :foo # start the actor's thread
209
+ Thread.new{ sleep(5); subject.shutdown }
210
+ subject.join(1)
211
+ stop = Time.now
212
+
213
+ stop.should >= start + 1
214
+ stop.should <= start + 2
215
+ end
216
+
217
+ it 'returns true when shutdown has completed before timeout' do
218
+ subject << :foo # start the actor's thread
219
+ Thread.new{ sleep(1); subject.shutdown }
220
+ subject.join.should be_true
221
+ end
222
+
223
+ it 'returns false on timeout' do
224
+ subject << :foo # start the actor's thread
225
+ Thread.new{ sleep(5); subject.shutdown }
226
+ subject.join(1).should be_false
227
+ end
228
+
229
+ it 'returns immediately when already shutdown' do
230
+ start = Time.now
231
+ subject << :foo # start the actor's thread
232
+ sleep(0.1)
233
+ subject.shutdown
234
+ sleep(0.1)
235
+
236
+ start = Time.now
237
+ subject.join
238
+ Time.now.should >= start
239
+ Time.now.should <= start + 0.1
240
+ end
241
+ end
242
+
243
+ context '#on_error' do
244
+
245
+ specify 'is not called on success' do
246
+ actor = subject.instance_variable_get(:@actor)
247
+ actor.should_not_receive(:on_error).with(any_args)
248
+ subject.post(:foo)
249
+ sleep(0.1)
250
+ end
251
+
252
+ specify 'is called when a message raises an exception' do
253
+ actor = subject.instance_variable_get(:@actor)
254
+ actor.should_receive(:on_error).
255
+ with(anything, [:poison], an_instance_of(StandardError))
256
+ subject.post(:poison)
257
+ sleep(0.1)
258
+ end
259
+ end
260
+
261
+ context 'observation' do
262
+
263
+ let(:observer_class) do
264
+ Class.new do
265
+ attr_reader :time, :msg, :value, :reason
266
+ def update(time, msg, value, reason)
267
+ @msg = msg
268
+ @time = time
269
+ @value = value
270
+ @reason = reason
271
+ end
272
+ end
273
+ end
274
+
275
+ it 'notifies observers' do
276
+ o1 = observer_class.new
277
+ o2 = observer_class.new
278
+
279
+ subject.add_observer(o1)
280
+ subject.add_observer(o2)
281
+
282
+ subject << :foo
283
+ sleep(0.1)
284
+
285
+ o1.value.should eq :foo
286
+ o1.reason.should be_nil
287
+
288
+ o2.value.should eq :foo
289
+ o2.reason.should be_nil
290
+ end
291
+
292
+ it 'does not notify removed observers' do
293
+ o1 = observer_class.new
294
+ o2 = observer_class.new
295
+
296
+ subject.add_observer(o1)
297
+ subject.add_observer(o2)
298
+
299
+ subject << :foo
300
+ sleep(0.1)
301
+
302
+ subject.delete_observer(o1)
303
+ subject << :bar
304
+ sleep(0.1)
305
+ o1.value.should_not eq :bar
306
+
307
+ subject.delete_observers
308
+ subject << :baz
309
+ sleep(0.1)
310
+ o1.value.should_not eq :baz
311
+ end
312
+ end
313
+ end
@@ -6,6 +6,12 @@ module Concurrent
6
6
 
7
7
  describe Actor do
8
8
 
9
+ before do
10
+ # suppress deprecation warnings.
11
+ Concurrent::Actor.any_instance.stub(:warn)
12
+ Concurrent::Actor.stub(:warn)
13
+ end
14
+
9
15
  let(:actor_class) do
10
16
  Class.new(Actor) do
11
17
  attr_reader :last_message
@@ -127,6 +133,7 @@ module Concurrent
127
133
  end
128
134
 
129
135
  it 'notifies observers when a message is successfully handled' do
136
+ pending('intermittently failing; deprecated')
130
137
  observer.should_receive(:update).exactly(10).times.with(any_args())
131
138
  subject.add_observer(observer)
132
139
  @thread = Thread.new{ subject.run }
@@ -196,7 +203,7 @@ module Concurrent
196
203
  it 'passes a duplicate of the given block to each actor in the pool' do
197
204
  block = proc{ nil }
198
205
  block.should_receive(:dup).exactly(5).times.and_return(proc{ nil })
199
- mailbox, pool = Channel.pool(5, &block)
206
+ mailbox, pool = clazz.pool(5, &block)
200
207
  end
201
208
 
202
209
  it 'gives all pool the same mailbox' do
@@ -237,6 +244,7 @@ module Concurrent
237
244
  end
238
245
 
239
246
  it 'posts to the mailbox with Poolbox#<<' do
247
+ pending('intermittently failing; deprecated')
240
248
  @expected = false
241
249
  mailbox, pool = clazz.pool(1)
242
250
  @thread = Thread.new{ pool.first.run }
@@ -1,12 +1,13 @@
1
1
  require 'spec_helper'
2
2
  require_relative 'dereferenceable_shared'
3
- require_relative 'uses_global_thread_pool_shared'
4
3
 
5
4
  module Concurrent
6
5
 
7
6
  describe Agent do
8
7
 
9
- subject { Agent.new(0) }
8
+ let(:executor) { PerThreadExecutor.new }
9
+
10
+ subject { Agent.new(0, executor: executor) }
10
11
 
11
12
  let(:observer) do
12
13
  Class.new do
@@ -17,30 +18,22 @@ module Concurrent
17
18
  end.new
18
19
  end
19
20
 
20
- before(:each) do
21
- Agent.thread_pool = FixedThreadPool.new(1)
22
- end
23
-
24
21
  context 'behavior' do
25
22
 
26
- # uses_global_thread_pool
27
-
28
- let!(:thread_pool_user) { Agent }
29
-
30
- it_should_behave_like Concurrent::UsesGlobalThreadPool
31
-
32
23
  # dereferenceable
33
24
 
34
25
  def dereferenceable_subject(value, opts = {})
26
+ opts = opts.merge(executor: executor)
35
27
  Agent.new(value, opts)
36
28
  end
37
29
 
38
30
  def dereferenceable_observable(opts = {})
31
+ opts = opts.merge(executor: executor)
39
32
  Agent.new(0, opts)
40
33
  end
41
34
 
42
35
  def execute_dereferenceable(subject)
43
- subject << proc { 'value' }
36
+ subject.post{|value| 10 }
44
37
  sleep(0.1)
45
38
  end
46
39
 
@@ -49,6 +42,8 @@ module Concurrent
49
42
 
50
43
  context '#initialize' do
51
44
 
45
+ let(:executor) { ImmediateExecutor.new }
46
+
52
47
  it 'sets the value to the given initial state' do
53
48
  Agent.new(10).value.should eq 10
54
49
  end
@@ -60,6 +55,30 @@ module Concurrent
60
55
  it 'sets the timeout to the default when nil' do
61
56
  Agent.new(0).timeout.should eq Agent::TIMEOUT
62
57
  end
58
+
59
+ it 'uses the executor given with the :executor option' do
60
+ executor.should_receive(:post).with(any_args).and_return(0)
61
+ agent = Agent.new(0, executor: executor)
62
+ agent.post{|value| 0 }
63
+ end
64
+
65
+ it 'uses the global operation pool when :operation is true' do
66
+ Concurrent.configuration.should_receive(:global_operation_pool).and_return(executor)
67
+ agent = Agent.new(0, operation: true)
68
+ agent.post{|value| 0 }
69
+ end
70
+
71
+ it 'uses the global task pool when :task is true' do
72
+ Concurrent.configuration.should_receive(:global_task_pool).and_return(executor)
73
+ agent = Agent.new(0, task: true)
74
+ agent.post{|value| 0 }
75
+ end
76
+
77
+ it 'uses the global task pool by default' do
78
+ Concurrent.configuration.should_receive(:global_task_pool).and_return(executor)
79
+ agent = Agent.new(0)
80
+ agent.post{|value| 0 }
81
+ end
63
82
  end
64
83
 
65
84
  context '#rescue' do
@@ -116,7 +135,7 @@ module Concurrent
116
135
  context '#post' do
117
136
 
118
137
  it 'adds the given block to the queue' do
119
- Agent.thread_pool.should_receive(:post).with(no_args).exactly(3).times
138
+ executor.should_receive(:post).with(no_args).exactly(3).times
120
139
  subject.post { sleep(100) }
121
140
  subject.post { nil }
122
141
  subject.post { nil }
@@ -124,7 +143,7 @@ module Concurrent
124
143
  end
125
144
 
126
145
  it 'does not add to the queue when no block is given' do
127
- Agent.thread_pool.should_receive(:post).with(no_args).exactly(2).times
146
+ executor.should_receive(:post).with(no_args).exactly(2).times
128
147
  subject.post { sleep(100) }
129
148
  subject.post
130
149
  subject.post { nil }
@@ -140,12 +159,12 @@ module Concurrent
140
159
  subject.post { @expected << 2 }
141
160
  subject.post { @expected << 3 }
142
161
  sleep(0.1)
143
- @expected.should eq [1, 2, 3]
162
+ @expected.sort.should eq [1, 2, 3]
144
163
  end
145
164
 
146
165
  it 'passes the current value to the handler' do
147
166
  @expected = nil
148
- Agent.new(10).post { |i| @expected = i }
167
+ Agent.new(10, executor: executor).post { |i| @expected = i }
149
168
  sleep(0.1)
150
169
  @expected.should eq 10
151
170
  end
@@ -157,7 +176,7 @@ module Concurrent
157
176
  end
158
177
 
159
178
  it 'rejects the handler after timeout reached' do
160
- agent = Agent.new(0, timeout: 0.1)
179
+ agent = Agent.new(0, timeout: 0.1, executor: executor)
161
180
  agent.post { sleep(1); 10 }
162
181
  agent.value.should eq 0
163
182
  end
@@ -182,21 +201,21 @@ module Concurrent
182
201
  end
183
202
 
184
203
  it 'sets the new value when the validator returns true' do
185
- agent = Agent.new(0).validate { true }
204
+ agent = Agent.new(0, executor: executor).validate { true }
186
205
  agent.post { 10 }
187
206
  sleep(0.1)
188
207
  agent.value.should eq 10
189
208
  end
190
209
 
191
210
  it 'does not change the value when the validator returns false' do
192
- agent = Agent.new(0).validate { false }
211
+ agent = Agent.new(0, executor: executor).validate { false }
193
212
  agent.post { 10 }
194
213
  sleep(0.1)
195
214
  agent.value.should eq 0
196
215
  end
197
216
 
198
217
  it 'does not change the value when the validator raises an exception' do
199
- agent = Agent.new(0).validate { raise StandardError }
218
+ agent = Agent.new(0, executor: executor).validate { raise StandardError }
200
219
  agent.post { 10 }
201
220
  sleep(0.1)
202
221
  agent.value.should eq 0
@@ -208,85 +227,85 @@ module Concurrent
208
227
  it 'calls the first exception block with a matching class' do
209
228
  @expected = nil
210
229
  subject.
211
- rescue(StandardError) { |ex| @expected = 1 }.
212
- rescue(StandardError) { |ex| @expected = 2 }.
213
- rescue(StandardError) { |ex| @expected = 3 }
214
- subject.post { raise StandardError }
215
- sleep(0.1)
216
- @expected.should eq 1
217
- end
230
+ rescue(StandardError) { |ex| @expected = 1 }.
231
+ rescue(StandardError) { |ex| @expected = 2 }.
232
+ rescue(StandardError) { |ex| @expected = 3 }
233
+ subject.post { raise StandardError }
234
+ sleep(0.1)
235
+ @expected.should eq 1
236
+ end
218
237
 
219
238
  it 'matches all with a rescue with no class given' do
220
239
  @expected = nil
221
240
  subject.
222
- rescue(LoadError) { |ex| @expected = 1 }.
223
- rescue { |ex| @expected = 2 }.
224
- rescue(StandardError) { |ex| @expected = 3 }
225
- subject.post { raise NoMethodError }
226
- sleep(0.1)
227
- @expected.should eq 2
228
- end
241
+ rescue(LoadError) { |ex| @expected = 1 }.
242
+ rescue { |ex| @expected = 2 }.
243
+ rescue(StandardError) { |ex| @expected = 3 }
244
+ subject.post { raise NoMethodError }
245
+ sleep(0.1)
246
+ @expected.should eq 2
247
+ end
229
248
 
230
249
  it 'searches associated rescue handlers in order' do
231
250
  @expected = nil
232
251
  subject.
233
- rescue(ArgumentError) { |ex| @expected = 1 }.
234
- rescue(LoadError) { |ex| @expected = 2 }.
235
- rescue(StandardError) { |ex| @expected = 3 }
236
- subject.post { raise ArgumentError }
237
- sleep(0.1)
238
- @expected.should eq 1
252
+ rescue(ArgumentError) { |ex| @expected = 1 }.
253
+ rescue(LoadError) { |ex| @expected = 2 }.
254
+ rescue(StandardError) { |ex| @expected = 3 }
255
+ subject.post { raise ArgumentError }
256
+ sleep(0.1)
257
+ @expected.should eq 1
239
258
 
240
- @expected = nil
241
- subject.
242
- rescue(ArgumentError) { |ex| @expected = 1 }.
259
+ @expected = nil
260
+ subject.
261
+ rescue(ArgumentError) { |ex| @expected = 1 }.
243
262
  rescue(LoadError) { |ex| @expected = 2 }.
244
263
  rescue(StandardError) { |ex| @expected = 3 }
245
- subject.post { raise LoadError }
246
- sleep(0.1)
247
- @expected.should eq 2
264
+ subject.post { raise LoadError }
265
+ sleep(0.1)
266
+ @expected.should eq 2
248
267
 
249
- @expected = nil
250
- subject.
268
+ @expected = nil
269
+ subject.
251
270
  rescue(ArgumentError) { |ex| @expected = 1 }.
252
- rescue(LoadError) { |ex| @expected = 2 }.
253
- rescue(StandardError) { |ex| @expected = 3 }
254
- subject.post { raise StandardError }
255
- sleep(0.1)
256
- @expected.should eq 3
257
- end
271
+ rescue(LoadError) { |ex| @expected = 2 }.
272
+ rescue(StandardError) { |ex| @expected = 3 }
273
+ subject.post { raise StandardError }
274
+ sleep(0.1)
275
+ @expected.should eq 3
276
+ end
258
277
 
259
278
  it 'passes the exception object to the matched block' do
260
279
  @expected = nil
261
280
  subject.
262
- rescue(ArgumentError) { |ex| @expected = ex }.
263
- rescue(LoadError) { |ex| @expected = ex }.
264
- rescue(StandardError) { |ex| @expected = ex }
265
- subject.post { raise StandardError }
266
- sleep(0.1)
267
- @expected.should be_a(StandardError)
268
- end
281
+ rescue(ArgumentError) { |ex| @expected = ex }.
282
+ rescue(LoadError) { |ex| @expected = ex }.
283
+ rescue(StandardError) { |ex| @expected = ex }
284
+ subject.post { raise StandardError }
285
+ sleep(0.1)
286
+ @expected.should be_a(StandardError)
287
+ end
269
288
 
270
289
  it 'ignores rescuers without a block' do
271
290
  @expected = nil
272
291
  subject.
273
- rescue(StandardError).
274
- rescue(StandardError) { |ex| @expected = ex }
275
- subject.post { raise StandardError }
276
- sleep(0.1)
277
- @expected.should be_a(StandardError)
278
- end
292
+ rescue(StandardError).
293
+ rescue(StandardError) { |ex| @expected = ex }
294
+ subject.post { raise StandardError }
295
+ sleep(0.1)
296
+ @expected.should be_a(StandardError)
297
+ end
279
298
 
280
299
  it 'supresses the exception if no rescue matches' do
281
300
  lambda {
282
301
  subject.
283
- rescue(ArgumentError) { |ex| @expected = ex }.
284
- rescue(NotImplementedError) { |ex| @expected = ex }.
285
- rescue(NoMethodError) { |ex| @expected = ex }
302
+ rescue(ArgumentError) { |ex| @expected = ex }.
303
+ rescue(NotImplementedError) { |ex| @expected = ex }.
304
+ rescue(NoMethodError) { |ex| @expected = ex }
286
305
  subject.post { raise StandardError }
287
306
  sleep(0.1)
288
307
  }.should_not raise_error
289
- end
308
+ end
290
309
 
291
310
  it 'suppresses exceptions thrown from rescue handlers' do
292
311
  lambda {
@@ -300,7 +319,7 @@ module Concurrent
300
319
  context 'observation' do
301
320
 
302
321
  it 'notifies all observers when the value changes' do
303
- agent = Agent.new(0)
322
+ agent = Agent.new(0, executor: executor)
304
323
  agent.add_observer(observer)
305
324
  agent.post { 10 }
306
325
  sleep(0.1)
@@ -308,7 +327,7 @@ module Concurrent
308
327
  end
309
328
 
310
329
  it 'does not notify removed observers when the value changes' do
311
- agent = Agent.new(0)
330
+ agent = Agent.new(0, executor: executor)
312
331
  agent.add_observer(observer)
313
332
  agent.delete_observer(observer)
314
333
  agent.post { 10 }
@@ -317,7 +336,7 @@ module Concurrent
317
336
  end
318
337
 
319
338
  it 'does not notify observers when validation fails' do
320
- agent = Agent.new(0)
339
+ agent = Agent.new(0, executor: executor)
321
340
  agent.validate { false }
322
341
  agent.add_observer(observer)
323
342
  agent.post { 10 }
@@ -326,7 +345,7 @@ module Concurrent
326
345
  end
327
346
 
328
347
  it 'does not notify observers when the handler raises an exception' do
329
- agent = Agent.new(0)
348
+ agent = Agent.new(0, executor: executor)
330
349
  agent.add_observer(observer)
331
350
  agent.post { raise StandardError }
332
351
  sleep(0.1)
@@ -337,7 +356,7 @@ module Concurrent
337
356
  context 'aliases' do
338
357
 
339
358
  it 'aliases #deref for #value' do
340
- Agent.new(10).deref.should eq 10
359
+ Agent.new(10, executor: executor).deref.should eq 10
341
360
  end
342
361
 
343
362
  it 'aliases #validates for :validate' do
@@ -381,30 +400,12 @@ module Concurrent
381
400
  end
382
401
 
383
402
  it 'aliases #add_watch for #add_observer' do
384
- agent = Agent.new(0)
403
+ agent = Agent.new(0, executor: executor)
385
404
  agent.add_watch(observer)
386
405
  agent.post { 10 }
387
406
  sleep(0.1)
388
407
  observer.value.should eq 10
389
408
  end
390
409
  end
391
-
392
- context 'stress test' do
393
-
394
- before(:each) do
395
- Agent.thread_pool = FixedThreadPool.new(5)
396
- end
397
-
398
- specify do
399
- count = 10_000
400
- counter = Concurrent::Agent.new(0)
401
-
402
- count.times do |i|
403
- counter.post { |value| value + 1 }
404
- end
405
-
406
- sleep(0.1) until counter.value == count
407
- end
408
- end
409
410
  end
410
411
  end