concurrent-ruby 0.6.1 → 0.7.0.rc0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/concurrent.rb +3 -4
- data/lib/concurrent/atomic.rb +46 -0
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
- data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
- data/lib/concurrent/atomic_reference/jruby.rb +8 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
- data/lib/concurrent/atomic_reference/rbx.rb +16 -0
- data/lib/concurrent/atomic_reference/ruby.rb +16 -0
- data/lib/concurrent/atomics.rb +1 -1
- data/lib/concurrent/configuration.rb +1 -1
- data/lib/concurrent/supervisor.rb +1 -1
- data/lib/concurrent/timer_task.rb +0 -36
- data/lib/concurrent/version.rb +1 -1
- data/lib/concurrent_ruby_ext.so +0 -0
- data/lib/extension_helper.rb +9 -0
- metadata +16 -148
- data/lib/concurrent/actor/actor.rb +0 -270
- data/lib/concurrent/actor/postable.rb +0 -102
- data/lib/concurrent/actors.rb +0 -2
- data/lib/concurrent/atomic/atomic.rb +0 -48
- data/lib/concurrent/runnable.rb +0 -90
- data/lib/concurrent/stoppable.rb +0 -20
- data/spec/concurrent/actor/actor_spec.rb +0 -376
- data/spec/concurrent/actor/postable_shared.rb +0 -218
- data/spec/concurrent/actress_spec.rb +0 -211
- data/spec/concurrent/agent_spec.rb +0 -500
- data/spec/concurrent/async_spec.rb +0 -352
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +0 -172
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +0 -186
- data/spec/concurrent/atomic/atomic_spec.rb +0 -133
- data/spec/concurrent/atomic/condition_spec.rb +0 -171
- data/spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb +0 -10
- data/spec/concurrent/atomic/copy_on_write_observer_set_spec.rb +0 -10
- data/spec/concurrent/atomic/count_down_latch_spec.rb +0 -151
- data/spec/concurrent/atomic/cyclic_barrier_spec.rb +0 -248
- data/spec/concurrent/atomic/event_spec.rb +0 -200
- data/spec/concurrent/atomic/observer_set_shared.rb +0 -242
- data/spec/concurrent/atomic/thread_local_var_spec.rb +0 -113
- data/spec/concurrent/channel/buffered_channel_spec.rb +0 -151
- data/spec/concurrent/channel/channel_spec.rb +0 -39
- data/spec/concurrent/channel/probe_spec.rb +0 -77
- data/spec/concurrent/channel/unbuffered_channel_spec.rb +0 -132
- data/spec/concurrent/collection/blocking_ring_buffer_spec.rb +0 -149
- data/spec/concurrent/collection/priority_queue_spec.rb +0 -317
- data/spec/concurrent/collection/ring_buffer_spec.rb +0 -126
- data/spec/concurrent/configuration_spec.rb +0 -69
- data/spec/concurrent/dataflow_spec.rb +0 -242
- data/spec/concurrent/delay_spec.rb +0 -91
- data/spec/concurrent/dereferenceable_shared.rb +0 -146
- data/spec/concurrent/exchanger_spec.rb +0 -66
- data/spec/concurrent/executor/cached_thread_pool_shared.rb +0 -115
- data/spec/concurrent/executor/fixed_thread_pool_shared.rb +0 -136
- data/spec/concurrent/executor/global_thread_pool_shared.rb +0 -35
- data/spec/concurrent/executor/immediate_executor_spec.rb +0 -12
- data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +0 -44
- data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +0 -64
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +0 -21
- data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +0 -71
- data/spec/concurrent/executor/per_thread_executor_spec.rb +0 -57
- data/spec/concurrent/executor/ruby_cached_thread_pool_spec.rb +0 -69
- data/spec/concurrent/executor/ruby_fixed_thread_pool_spec.rb +0 -39
- data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +0 -18
- data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +0 -171
- data/spec/concurrent/executor/safe_task_executor_spec.rb +0 -103
- data/spec/concurrent/executor/thread_pool_class_cast_spec.rb +0 -52
- data/spec/concurrent/executor/thread_pool_executor_shared.rb +0 -155
- data/spec/concurrent/executor/thread_pool_shared.rb +0 -269
- data/spec/concurrent/executor/timer_set_spec.rb +0 -183
- data/spec/concurrent/future_spec.rb +0 -329
- data/spec/concurrent/ivar_spec.rb +0 -215
- data/spec/concurrent/mvar_spec.rb +0 -380
- data/spec/concurrent/obligation_shared.rb +0 -102
- data/spec/concurrent/obligation_spec.rb +0 -282
- data/spec/concurrent/observable_shared.rb +0 -177
- data/spec/concurrent/observable_spec.rb +0 -56
- data/spec/concurrent/options_parser_spec.rb +0 -71
- data/spec/concurrent/promise_spec.rb +0 -367
- data/spec/concurrent/runnable_shared.rb +0 -68
- data/spec/concurrent/runnable_spec.rb +0 -235
- data/spec/concurrent/scheduled_task_spec.rb +0 -340
- data/spec/concurrent/stoppable_shared.rb +0 -37
- data/spec/concurrent/supervisor_spec.rb +0 -1149
- data/spec/concurrent/timer_task_spec.rb +0 -256
- data/spec/concurrent/tvar_spec.rb +0 -137
- data/spec/concurrent/utility/processor_count_spec.rb +0 -20
- data/spec/concurrent/utility/timeout_spec.rb +0 -50
- data/spec/concurrent/utility/timer_spec.rb +0 -52
- data/spec/spec_helper.rb +0 -41
- data/spec/support/example_group_extensions.rb +0 -52
- data/spec/support/less_than_or_equal_to_matcher.rb +0 -5
data/lib/concurrent/stoppable.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'concurrent/runnable'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
module Stoppable
|
6
|
-
|
7
|
-
def before_stop(&block)
|
8
|
-
raise ArgumentError.new('no block given') unless block_given?
|
9
|
-
raise Runnable::LifecycleError.new('#before_stop already set') if @before_stop_proc
|
10
|
-
@before_stop_proc = block
|
11
|
-
self
|
12
|
-
end
|
13
|
-
|
14
|
-
protected
|
15
|
-
|
16
|
-
def before_stop_proc
|
17
|
-
@before_stop_proc
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,376 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require_relative 'postable_shared'
|
3
|
-
require_relative '../runnable_shared'
|
4
|
-
|
5
|
-
module Concurrent
|
6
|
-
|
7
|
-
describe Actor do
|
8
|
-
|
9
|
-
before do
|
10
|
-
# suppress deprecation warnings.
|
11
|
-
Concurrent::Actor.any_instance.stub(:warn)
|
12
|
-
Concurrent::Actor.stub(:warn)
|
13
|
-
end
|
14
|
-
|
15
|
-
let(:actor_class) do
|
16
|
-
Class.new(Actor) do
|
17
|
-
attr_reader :last_message
|
18
|
-
def initialize(&block)
|
19
|
-
@task = block
|
20
|
-
super()
|
21
|
-
end
|
22
|
-
def act(*message)
|
23
|
-
@last_message = message
|
24
|
-
@task.call(*message) unless @task.nil?
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
## :runnable
|
30
|
-
subject { Class.new(actor_class).new }
|
31
|
-
it_should_behave_like :runnable
|
32
|
-
|
33
|
-
## :postable
|
34
|
-
|
35
|
-
let!(:postable_class){ actor_class }
|
36
|
-
|
37
|
-
let(:sender_class) do
|
38
|
-
Class.new(Actor) do
|
39
|
-
def act(*message)
|
40
|
-
if message.first.is_a?(Exception)
|
41
|
-
raise message.first
|
42
|
-
else
|
43
|
-
return message.first
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
let(:sender) { sender_class.new }
|
50
|
-
let(:receiver) { postable_class.new }
|
51
|
-
|
52
|
-
it_should_behave_like :postable
|
53
|
-
|
54
|
-
context '#run' do
|
55
|
-
|
56
|
-
it 'empties the queue' do
|
57
|
-
@thread = Thread.new{ subject.run }
|
58
|
-
@thread.join(0.1)
|
59
|
-
q = subject.instance_variable_get(:@queue)
|
60
|
-
q.size.should == 0
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
context '#stop' do
|
65
|
-
|
66
|
-
it 'empties the queue' do
|
67
|
-
actor = actor_class.new{|msg| sleep }
|
68
|
-
@thread = Thread.new{ actor.run }
|
69
|
-
10.times { actor.post(true) }
|
70
|
-
@thread.join(0.1)
|
71
|
-
actor.stop
|
72
|
-
@thread.join(0.1)
|
73
|
-
q = actor.instance_variable_get(:@queue)
|
74
|
-
if q.size >= 1
|
75
|
-
q.pop.should == :stop
|
76
|
-
else
|
77
|
-
q.size.should == 0
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'pushes a :stop message onto the queue' do
|
82
|
-
@thread = Thread.new{ subject.run }
|
83
|
-
@thread.join(0.1)
|
84
|
-
q = subject.instance_variable_get(:@queue)
|
85
|
-
q.should_receive(:push).once.with(:stop)
|
86
|
-
subject.stop
|
87
|
-
@thread.join(0.1)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
context 'exception handling' do
|
92
|
-
|
93
|
-
#it 'supresses exceptions thrown when handling messages' do
|
94
|
-
#pending('intermittently failing; deprecated')
|
95
|
-
#actor = actor_class.new{|msg| raise StandardError }
|
96
|
-
#@thread = Thread.new{ actor.run }
|
97
|
-
#expect {
|
98
|
-
#@thread.join(0.1)
|
99
|
-
#10.times { actor.post(true) }
|
100
|
-
#}.not_to raise_error
|
101
|
-
#actor.stop
|
102
|
-
#end
|
103
|
-
end
|
104
|
-
|
105
|
-
context 'observation' do
|
106
|
-
|
107
|
-
let(:actor_class) do
|
108
|
-
Class.new(Actor) do
|
109
|
-
def act(*message)
|
110
|
-
if message.first.is_a?(Exception)
|
111
|
-
raise message.first
|
112
|
-
else
|
113
|
-
return message.first
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
subject { Class.new(actor_class).new }
|
120
|
-
|
121
|
-
let(:observer) do
|
122
|
-
Class.new {
|
123
|
-
attr_reader :time
|
124
|
-
attr_reader :message
|
125
|
-
attr_reader :value
|
126
|
-
attr_reader :reason
|
127
|
-
def update(time, message, value, reason)
|
128
|
-
@time = time
|
129
|
-
@message = message
|
130
|
-
@value = value
|
131
|
-
@reason = reason
|
132
|
-
end
|
133
|
-
}.new
|
134
|
-
end
|
135
|
-
|
136
|
-
#it 'notifies observers when a message is successfully handled' do
|
137
|
-
#pending('intermittently failing; deprecated')
|
138
|
-
#observer.should_receive(:update).exactly(10).times.with(any_args())
|
139
|
-
#subject.add_observer(observer)
|
140
|
-
#@thread = Thread.new{ subject.run }
|
141
|
-
#@thread.join(0.1)
|
142
|
-
#10.times { subject.post(42) }
|
143
|
-
#@thread.join(0.1)
|
144
|
-
#end
|
145
|
-
|
146
|
-
#it 'notifies observers when a message raises an exception' do
|
147
|
-
#pending('intermittently failing; deprecated')
|
148
|
-
#error = StandardError.new
|
149
|
-
#observer.should_receive(:update).exactly(10).times.with(any_args())
|
150
|
-
#subject.add_observer(observer)
|
151
|
-
#@thread = Thread.new{ subject.run }
|
152
|
-
#@thread.join(0.1)
|
153
|
-
#10.times { subject.post(error) }
|
154
|
-
#@thread.join(0.1)
|
155
|
-
#end
|
156
|
-
|
157
|
-
it 'passes the time, message, value, and reason to the observer on success' do
|
158
|
-
subject.add_observer(observer)
|
159
|
-
@thread = Thread.new{ subject.run }
|
160
|
-
@thread.join(0.1)
|
161
|
-
subject.post(42)
|
162
|
-
@thread.join(0.1)
|
163
|
-
|
164
|
-
observer.time.should be_a(Time)
|
165
|
-
observer.message.should eq [42]
|
166
|
-
observer.value.should eq 42
|
167
|
-
observer.reason.should be_nil
|
168
|
-
end
|
169
|
-
|
170
|
-
it 'passes the time, message, value, and reason to the observer on exception' do
|
171
|
-
error = StandardError.new
|
172
|
-
subject.add_observer(observer)
|
173
|
-
@thread = Thread.new{ subject.run }
|
174
|
-
@thread.join(0.1)
|
175
|
-
subject.post(error)
|
176
|
-
@thread.join(0.1)
|
177
|
-
|
178
|
-
observer.time.should be_a(Time)
|
179
|
-
observer.message.should eq [error]
|
180
|
-
observer.value.should be_nil
|
181
|
-
observer.reason.should be_a(Exception)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
context '#pool' do
|
186
|
-
|
187
|
-
let(:clazz){ Class.new(actor_class) }
|
188
|
-
|
189
|
-
it 'raises an exception if the count is zero or less' do
|
190
|
-
expect {
|
191
|
-
clazz.pool(0)
|
192
|
-
}.to raise_error(ArgumentError)
|
193
|
-
end
|
194
|
-
|
195
|
-
it 'creates the requested number of pool' do
|
196
|
-
mailbox, pool = clazz.pool(5)
|
197
|
-
pool.size.should == 5
|
198
|
-
end
|
199
|
-
|
200
|
-
it 'passes all optional arguments to the individual constructors' do
|
201
|
-
clazz.should_receive(:new).with(1, 2, 3).exactly(5).times
|
202
|
-
clazz.pool(5, 1, 2, 3)
|
203
|
-
end
|
204
|
-
|
205
|
-
it 'passes a duplicate of the given block to each actor in the pool' do
|
206
|
-
block = proc{ nil }
|
207
|
-
block.should_receive(:dup).exactly(5).times.and_return(proc{ nil })
|
208
|
-
mailbox, pool = clazz.pool(5, &block)
|
209
|
-
end
|
210
|
-
|
211
|
-
it 'gives all pool the same mailbox' do
|
212
|
-
mailbox, pool = clazz.pool(2)
|
213
|
-
mbox1 = pool.first.instance_variable_get(:@queue)
|
214
|
-
mbox2 = pool.last.instance_variable_get(:@queue)
|
215
|
-
mbox1.should eq mbox2
|
216
|
-
end
|
217
|
-
|
218
|
-
it 'returns a Poolbox as the first retval' do
|
219
|
-
mailbox, pool = clazz.pool(2)
|
220
|
-
mailbox.should be_a(Actor::Poolbox)
|
221
|
-
end
|
222
|
-
|
223
|
-
it 'gives the Poolbox the same mailbox as the pool' do
|
224
|
-
mailbox, pool = clazz.pool(1)
|
225
|
-
mbox1 = mailbox.instance_variable_get(:@queue)
|
226
|
-
mbox2 = pool.first.instance_variable_get(:@queue)
|
227
|
-
mbox1.should eq mbox2
|
228
|
-
end
|
229
|
-
|
230
|
-
it 'returns an array of pool as the second retval' do
|
231
|
-
mailbox, pool = clazz.pool(2)
|
232
|
-
pool.each do |actor|
|
233
|
-
actor.should be_a(clazz)
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
it 'posts to the mailbox with Poolbox#post' do
|
238
|
-
mailbox, pool = clazz.pool(1)
|
239
|
-
@thread = Thread.new{ pool.first.run }
|
240
|
-
sleep(0.1)
|
241
|
-
mailbox.post(42)
|
242
|
-
sleep(0.1)
|
243
|
-
pool.first.last_message.should eq [42]
|
244
|
-
pool.first.stop
|
245
|
-
@thread.kill
|
246
|
-
end
|
247
|
-
|
248
|
-
#it 'posts to the mailbox with Poolbox#<<' do
|
249
|
-
#pending('intermittently failing; deprecated')
|
250
|
-
#@expected = false
|
251
|
-
#mailbox, pool = clazz.pool(1)
|
252
|
-
#@thread = Thread.new{ pool.first.run }
|
253
|
-
#sleep(0.1)
|
254
|
-
#mailbox << 42
|
255
|
-
#sleep(0.1)
|
256
|
-
#pool.first.last_message.should eq [42]
|
257
|
-
#pool.first.stop
|
258
|
-
#@thread.kill
|
259
|
-
#end
|
260
|
-
end
|
261
|
-
|
262
|
-
context 'subclassing' do
|
263
|
-
|
264
|
-
after(:each) do
|
265
|
-
@thread.kill unless @thread.nil?
|
266
|
-
end
|
267
|
-
|
268
|
-
context '#pool' do
|
269
|
-
|
270
|
-
it 'creates pool of the appropriate subclass' do
|
271
|
-
actor = Class.new(actor_class)
|
272
|
-
mailbox, pool = actor.pool(1)
|
273
|
-
pool.first.should be_a(actor)
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
context '#act overloading' do
|
278
|
-
|
279
|
-
it 'raises an exception if #act is not implemented in the subclass' do
|
280
|
-
actor = Class.new(Actor).new
|
281
|
-
@thread = Thread.new{ actor.run }
|
282
|
-
@thread.join(0.1)
|
283
|
-
expect {
|
284
|
-
actor.post(:foo)
|
285
|
-
@thread.join(0.1)
|
286
|
-
}.to raise_error(NotImplementedError)
|
287
|
-
actor.stop
|
288
|
-
end
|
289
|
-
|
290
|
-
it 'uses the subclass #act implementation' do
|
291
|
-
actor = actor_class.new{|*args| @expected = true }
|
292
|
-
@thread = Thread.new{ actor.run }
|
293
|
-
@thread.join(0.1)
|
294
|
-
actor.post(:foo)
|
295
|
-
@thread.join(0.1)
|
296
|
-
actor.last_message.should eq [:foo]
|
297
|
-
actor.stop
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
context '#on_error overloading' do
|
302
|
-
|
303
|
-
let(:bad_actor) do
|
304
|
-
Class.new(actor_class) {
|
305
|
-
attr_reader :last_error
|
306
|
-
def act(*message)
|
307
|
-
raise StandardError
|
308
|
-
end
|
309
|
-
def on_error(*args)
|
310
|
-
@last_error = args
|
311
|
-
end
|
312
|
-
}
|
313
|
-
end
|
314
|
-
|
315
|
-
it 'uses the subclass #on_error implementation' do
|
316
|
-
actor = bad_actor.new
|
317
|
-
@thread = Thread.new{ actor.run }
|
318
|
-
@thread.join(0.1)
|
319
|
-
actor.post(42)
|
320
|
-
@thread.join(0.1)
|
321
|
-
actor.last_error[0].should be_a(Time)
|
322
|
-
actor.last_error[1].should eq [42]
|
323
|
-
actor.last_error[2].should be_a(StandardError)
|
324
|
-
actor.stop
|
325
|
-
end
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
context 'supervision' do
|
330
|
-
|
331
|
-
it 'can be started by a Supervisor' do
|
332
|
-
actor = actor_class.new
|
333
|
-
supervisor = Supervisor.new
|
334
|
-
supervisor.add_worker(actor)
|
335
|
-
|
336
|
-
actor.should_receive(:run).with(no_args())
|
337
|
-
supervisor.run!
|
338
|
-
sleep(0.1)
|
339
|
-
|
340
|
-
supervisor.stop
|
341
|
-
sleep(0.1)
|
342
|
-
actor.stop
|
343
|
-
end
|
344
|
-
|
345
|
-
it 'can receive messages while under supervision' do
|
346
|
-
@expected = false
|
347
|
-
actor = actor_class.new{|*args| @expected = true}
|
348
|
-
supervisor = Supervisor.new
|
349
|
-
supervisor.add_worker(actor)
|
350
|
-
supervisor.run!
|
351
|
-
sleep(0.1)
|
352
|
-
|
353
|
-
actor.post(42)
|
354
|
-
sleep(0.1)
|
355
|
-
@expected.should be_true
|
356
|
-
|
357
|
-
supervisor.stop
|
358
|
-
sleep(0.1)
|
359
|
-
actor.stop
|
360
|
-
end
|
361
|
-
|
362
|
-
it 'can be stopped by a supervisor' do
|
363
|
-
actor = actor_class.new
|
364
|
-
supervisor = Supervisor.new
|
365
|
-
supervisor.add_worker(actor)
|
366
|
-
|
367
|
-
supervisor.run!
|
368
|
-
sleep(0.1)
|
369
|
-
|
370
|
-
actor.should_receive(:stop).with(no_args())
|
371
|
-
supervisor.stop
|
372
|
-
sleep(0.1)
|
373
|
-
end
|
374
|
-
end
|
375
|
-
end
|
376
|
-
end
|
@@ -1,218 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
share_examples_for :postable do
|
4
|
-
|
5
|
-
after(:each) do
|
6
|
-
subject.stop
|
7
|
-
@thread.kill unless @thread.nil?
|
8
|
-
sleep(0.1)
|
9
|
-
end
|
10
|
-
|
11
|
-
context '#post' do
|
12
|
-
|
13
|
-
it 'raises an exception when the message is empty' do
|
14
|
-
expect {
|
15
|
-
subject.post
|
16
|
-
}.to raise_error(ArgumentError)
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'returns false when not running' do
|
20
|
-
subject.post(42).should be_false
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'pushes a message onto the queue' do
|
24
|
-
@expected = false
|
25
|
-
postable = postable_class.new{|msg| @expected = msg }
|
26
|
-
@thread = Thread.new{ postable.run }
|
27
|
-
@thread.join(0.1)
|
28
|
-
postable.post(true)
|
29
|
-
@thread.join(0.1)
|
30
|
-
@expected.should be_true
|
31
|
-
postable.stop
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'returns true on success' do
|
35
|
-
postable = postable_class.new{|msg| sleep }
|
36
|
-
@thread = Thread.new{ postable.run }
|
37
|
-
5.times{ @thread.join(0.1); break if postable.running? }
|
38
|
-
postable.post(true).should be_true
|
39
|
-
postable.stop
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'is aliased a <<' do
|
43
|
-
@expected = false
|
44
|
-
postable = postable_class.new{|msg| @expected = msg }
|
45
|
-
@thread = Thread.new{ postable.run }
|
46
|
-
@thread.join(0.1)
|
47
|
-
postable << true
|
48
|
-
@thread.join(0.1)
|
49
|
-
@expected.should be_true
|
50
|
-
postable.stop
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
context '#post?' do
|
55
|
-
|
56
|
-
it 'returns nil when not running' do
|
57
|
-
subject.post?(42).should be_false
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'returns an Obligation' do
|
61
|
-
postable = postable_class.new{ nil }
|
62
|
-
@thread = Thread.new{ postable.run }
|
63
|
-
@thread.join(0.1)
|
64
|
-
obligation = postable.post?(nil)
|
65
|
-
obligation.should be_a(Concurrent::Obligation)
|
66
|
-
postable.stop
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'fulfills the obligation on success' do
|
70
|
-
postable = postable_class.new{|msg| @expected = msg }
|
71
|
-
@thread = Thread.new{ postable.run }
|
72
|
-
@thread.join(0.1)
|
73
|
-
obligation = postable.post?(42)
|
74
|
-
@thread.join(0.1)
|
75
|
-
obligation.should be_fulfilled
|
76
|
-
obligation.value.should == 42
|
77
|
-
postable.stop
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'rejects the obligation on failure' do
|
81
|
-
postable = postable_class.new{|msg| raise StandardError.new('Boom!') }
|
82
|
-
@thread = Thread.new{ postable.run }
|
83
|
-
@thread.join(0.1)
|
84
|
-
obligation = postable.post?(42)
|
85
|
-
@thread.join(0.1)
|
86
|
-
obligation.should be_rejected
|
87
|
-
obligation.reason.should be_a(StandardError)
|
88
|
-
postable.stop
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
context '#post!' do
|
93
|
-
|
94
|
-
it 'raises Concurrent::Runnable::LifecycleError when not running' do
|
95
|
-
expect {
|
96
|
-
subject.post!(1, 'Hello World!')
|
97
|
-
}.to raise_error(Concurrent::LifecycleError)
|
98
|
-
end
|
99
|
-
|
100
|
-
it 'blocks for up to the given number of seconds' do
|
101
|
-
postable = postable_class.new{|msg| sleep }
|
102
|
-
@thread = Thread.new{ postable.run }
|
103
|
-
@thread.join(0.1)
|
104
|
-
start = Time.now.to_i
|
105
|
-
expect {
|
106
|
-
postable.post!(2, nil)
|
107
|
-
}.to raise_error
|
108
|
-
elapsed = Time.now.to_i - start
|
109
|
-
elapsed.should >= 2
|
110
|
-
postable.stop
|
111
|
-
end
|
112
|
-
|
113
|
-
it 'raises Concurrent::TimeoutError when seconds is zero' do
|
114
|
-
postable = postable_class.new{|msg| 42 }
|
115
|
-
@thread = Thread.new{ postable.run }
|
116
|
-
@thread.join(0.1)
|
117
|
-
expect {
|
118
|
-
postable.post!(0, nil)
|
119
|
-
}.to raise_error(Concurrent::TimeoutError)
|
120
|
-
postable.stop
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'raises Concurrent::TimeoutError on timeout' do
|
124
|
-
postable = postable_class.new{|msg| sleep }
|
125
|
-
@thread = Thread.new{ postable.run }
|
126
|
-
@thread.join(0.1)
|
127
|
-
expect {
|
128
|
-
postable.post!(1, nil)
|
129
|
-
}.to raise_error(Concurrent::TimeoutError)
|
130
|
-
postable.stop
|
131
|
-
end
|
132
|
-
|
133
|
-
it 'bubbles the exception on error' do
|
134
|
-
postable = postable_class.new{|msg| raise StandardError.new('Boom!') }
|
135
|
-
@thread = Thread.new{ postable.run }
|
136
|
-
@thread.join(0.1)
|
137
|
-
expect {
|
138
|
-
postable.post!(1, nil)
|
139
|
-
}.to raise_error(StandardError)
|
140
|
-
postable.stop
|
141
|
-
end
|
142
|
-
|
143
|
-
it 'returns the result on success' do
|
144
|
-
postable = postable_class.new{|msg| 42 }
|
145
|
-
@thread = Thread.new{ postable.run }
|
146
|
-
@thread.join(0.1)
|
147
|
-
expected = postable.post!(1, nil)
|
148
|
-
expected.should == 42
|
149
|
-
postable.stop
|
150
|
-
end
|
151
|
-
|
152
|
-
it 'attempts to cancel the operation on timeout' do
|
153
|
-
@expected = 0
|
154
|
-
postable = postable_class.new{|msg| sleep(0.5); @expected += 1 }
|
155
|
-
@thread = Thread.new{ postable.run }
|
156
|
-
@thread.join(0.1)
|
157
|
-
postable.post(nil) # block the postable
|
158
|
-
expect {
|
159
|
-
postable.post!(0.1, nil)
|
160
|
-
}.to raise_error(Concurrent::TimeoutError)
|
161
|
-
sleep(1.5)
|
162
|
-
@expected.should == 1
|
163
|
-
postable.stop
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
context '#forward' do
|
168
|
-
|
169
|
-
let(:observer) { double('observer') }
|
170
|
-
|
171
|
-
before(:each) do
|
172
|
-
@sender = Thread.new{ sender.run }
|
173
|
-
@receiver = Thread.new{ receiver.run }
|
174
|
-
@sender.join(0.1)
|
175
|
-
@receiver.join(0.1)
|
176
|
-
end
|
177
|
-
|
178
|
-
after(:each) do
|
179
|
-
sender.stop
|
180
|
-
receiver.stop
|
181
|
-
sleep(0.1)
|
182
|
-
@sender.kill unless @sender.nil?
|
183
|
-
@receiver.kill unless @receiver.nil?
|
184
|
-
end
|
185
|
-
|
186
|
-
it 'returns false when sender not running' do
|
187
|
-
sender.stop
|
188
|
-
sleep(0.1)
|
189
|
-
sender.forward(receiver, 'Hello World!').should be_false
|
190
|
-
end
|
191
|
-
|
192
|
-
it 'forwards the result to the receiver on success' do
|
193
|
-
receiver.should_receive(:post).with(42)
|
194
|
-
sender.forward(receiver, 42)
|
195
|
-
sleep(0.1)
|
196
|
-
end
|
197
|
-
|
198
|
-
it 'does not forward on exception' do
|
199
|
-
receiver.should_not_receive(:post).with(42)
|
200
|
-
sender.forward(receiver, StandardError.new)
|
201
|
-
sleep(0.1)
|
202
|
-
end
|
203
|
-
|
204
|
-
it 'notifies observers on success' do
|
205
|
-
observer.should_receive(:update).with(any_args())
|
206
|
-
sender.add_observer(observer)
|
207
|
-
sender.forward(receiver, 42)
|
208
|
-
sleep(0.1)
|
209
|
-
end
|
210
|
-
|
211
|
-
it 'notifies observers on exception' do
|
212
|
-
observer.should_not_receive(:update).with(any_args())
|
213
|
-
sender.add_observer(observer)
|
214
|
-
sender.forward(receiver, StandardError.new)
|
215
|
-
sleep(0.1)
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|