concurrent-ruby 0.5.0 → 0.6.0.pre.1
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.
- checksums.yaml +4 -4
- data/README.md +88 -77
- data/lib/concurrent.rb +17 -2
- data/lib/concurrent/actor.rb +17 -0
- data/lib/concurrent/actor_context.rb +31 -0
- data/lib/concurrent/actor_ref.rb +39 -0
- data/lib/concurrent/agent.rb +12 -3
- data/lib/concurrent/async.rb +290 -0
- data/lib/concurrent/atomic.rb +5 -9
- data/lib/concurrent/cached_thread_pool.rb +39 -137
- data/lib/concurrent/channel/blocking_ring_buffer.rb +60 -0
- data/lib/concurrent/channel/buffered_channel.rb +83 -0
- data/lib/concurrent/channel/channel.rb +11 -0
- data/lib/concurrent/channel/probe.rb +19 -0
- data/lib/concurrent/channel/ring_buffer.rb +54 -0
- data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
- data/lib/concurrent/channel/waitable_list.rb +38 -0
- data/lib/concurrent/configuration.rb +92 -0
- data/lib/concurrent/dataflow.rb +9 -3
- data/lib/concurrent/delay.rb +88 -0
- data/lib/concurrent/exchanger.rb +31 -0
- data/lib/concurrent/fixed_thread_pool.rb +28 -122
- data/lib/concurrent/future.rb +10 -5
- data/lib/concurrent/immediate_executor.rb +3 -2
- data/lib/concurrent/ivar.rb +2 -1
- data/lib/concurrent/java_cached_thread_pool.rb +45 -0
- data/lib/concurrent/java_fixed_thread_pool.rb +37 -0
- data/lib/concurrent/java_thread_pool_executor.rb +194 -0
- data/lib/concurrent/per_thread_executor.rb +23 -0
- data/lib/concurrent/postable.rb +2 -0
- data/lib/concurrent/processor_count.rb +125 -0
- data/lib/concurrent/promise.rb +42 -18
- data/lib/concurrent/ruby_cached_thread_pool.rb +37 -0
- data/lib/concurrent/ruby_fixed_thread_pool.rb +31 -0
- data/lib/concurrent/ruby_thread_pool_executor.rb +268 -0
- data/lib/concurrent/ruby_thread_pool_worker.rb +69 -0
- data/lib/concurrent/simple_actor_ref.rb +124 -0
- data/lib/concurrent/thread_local_var.rb +1 -1
- data/lib/concurrent/thread_pool_executor.rb +30 -0
- data/lib/concurrent/timer_task.rb +13 -10
- data/lib/concurrent/tvar.rb +212 -0
- data/lib/concurrent/utilities.rb +1 -0
- data/lib/concurrent/version.rb +1 -1
- data/spec/concurrent/actor_context_spec.rb +37 -0
- data/spec/concurrent/actor_ref_shared.rb +313 -0
- data/spec/concurrent/actor_spec.rb +9 -1
- data/spec/concurrent/agent_spec.rb +97 -96
- data/spec/concurrent/async_spec.rb +320 -0
- data/spec/concurrent/cached_thread_pool_shared.rb +137 -0
- data/spec/concurrent/channel/blocking_ring_buffer_spec.rb +149 -0
- data/spec/concurrent/channel/buffered_channel_spec.rb +151 -0
- data/spec/concurrent/channel/channel_spec.rb +37 -0
- data/spec/concurrent/channel/probe_spec.rb +49 -0
- data/spec/concurrent/channel/ring_buffer_spec.rb +126 -0
- data/spec/concurrent/channel/unbuffered_channel_spec.rb +132 -0
- data/spec/concurrent/configuration_spec.rb +134 -0
- data/spec/concurrent/dataflow_spec.rb +109 -27
- data/spec/concurrent/delay_spec.rb +77 -0
- data/spec/concurrent/exchanger_spec.rb +66 -0
- data/spec/concurrent/fixed_thread_pool_shared.rb +136 -0
- data/spec/concurrent/future_spec.rb +60 -51
- data/spec/concurrent/global_thread_pool_shared.rb +33 -0
- data/spec/concurrent/immediate_executor_spec.rb +4 -25
- data/spec/concurrent/ivar_spec.rb +36 -23
- data/spec/concurrent/java_cached_thread_pool_spec.rb +64 -0
- data/spec/concurrent/java_fixed_thread_pool_spec.rb +64 -0
- data/spec/concurrent/java_thread_pool_executor_spec.rb +71 -0
- data/spec/concurrent/obligation_shared.rb +32 -20
- data/spec/concurrent/{global_thread_pool_spec.rb → per_thread_executor_spec.rb} +9 -13
- data/spec/concurrent/processor_count_spec.rb +20 -0
- data/spec/concurrent/promise_spec.rb +29 -41
- data/spec/concurrent/ruby_cached_thread_pool_spec.rb +69 -0
- data/spec/concurrent/ruby_fixed_thread_pool_spec.rb +39 -0
- data/spec/concurrent/ruby_thread_pool_executor_spec.rb +183 -0
- data/spec/concurrent/simple_actor_ref_spec.rb +219 -0
- data/spec/concurrent/thread_pool_class_cast_spec.rb +40 -0
- data/spec/concurrent/thread_pool_executor_shared.rb +155 -0
- data/spec/concurrent/thread_pool_shared.rb +98 -36
- data/spec/concurrent/tvar_spec.rb +137 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/functions.rb +4 -0
- metadata +85 -20
- data/lib/concurrent/cached_thread_pool/worker.rb +0 -91
- data/lib/concurrent/channel.rb +0 -63
- data/lib/concurrent/fixed_thread_pool/worker.rb +0 -54
- data/lib/concurrent/global_thread_pool.rb +0 -42
- data/spec/concurrent/cached_thread_pool_spec.rb +0 -101
- data/spec/concurrent/channel_spec.rb +0 -86
- data/spec/concurrent/fixed_thread_pool_spec.rb +0 -92
- data/spec/concurrent/uses_global_thread_pool_shared.rb +0 -64
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'actor_ref_shared'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
describe SimpleActorRef do
|
7
|
+
|
8
|
+
after(:each) do
|
9
|
+
subject.shutdown
|
10
|
+
sleep(0.1)
|
11
|
+
end
|
12
|
+
|
13
|
+
subject do
|
14
|
+
shared_actor_test_class.spawn
|
15
|
+
end
|
16
|
+
|
17
|
+
it_should_behave_like :actor_ref
|
18
|
+
|
19
|
+
context 'construction' do
|
20
|
+
|
21
|
+
it 'supports :args being nil' do
|
22
|
+
subject = shared_actor_test_class.spawn
|
23
|
+
actor = subject.instance_variable_get(:@actor)
|
24
|
+
actor.argv.should be_empty
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'passes all :args option to the actor constructor' do
|
28
|
+
subject = shared_actor_test_class.spawn(args: [1, 2, 3, 4])
|
29
|
+
actor = subject.instance_variable_get(:@actor)
|
30
|
+
actor.argv.should eq [1, 2, 3, 4]
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'passes the options hash to the ActorRef constructor' do
|
34
|
+
subject # prevent the after(:all) block from breaking this test
|
35
|
+
opts = {foo: :bar, hello: :world}
|
36
|
+
described_class.should_receive(:new).once.with(anything, opts)
|
37
|
+
shared_actor_test_class.spawn(opts)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'supervision' do
|
42
|
+
|
43
|
+
it 'does not start a new thread on construction' do
|
44
|
+
Thread.should_not_receive(:new).with(any_args)
|
45
|
+
subject = shared_actor_test_class.spawn
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'starts a new thread on the first post' do
|
49
|
+
thread = Thread.new{ nil }
|
50
|
+
Thread.should_receive(:new).once.with(no_args).and_return(thread)
|
51
|
+
subject << :foo
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'does not start a new thread after the first post' do
|
55
|
+
subject << :foo
|
56
|
+
sleep(0.1)
|
57
|
+
expected = Thread.list.length
|
58
|
+
5.times{ subject << :foo }
|
59
|
+
Thread.list.length.should eq expected
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'starts a new thread when the prior thread has died' do
|
63
|
+
subject << :foo
|
64
|
+
sleep(0.1)
|
65
|
+
|
66
|
+
subject << :terminate
|
67
|
+
sleep(0.1)
|
68
|
+
|
69
|
+
thread = Thread.new{ nil }
|
70
|
+
Thread.should_receive(:new).once.with(no_args).and_return(thread)
|
71
|
+
subject << :foo
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'does not reset the thread after shutdown' do
|
75
|
+
thread = Thread.new{ nil }
|
76
|
+
Thread.should_receive(:new).once.with(no_args).and_return(thread)
|
77
|
+
subject << :foo
|
78
|
+
sleep(0.1)
|
79
|
+
|
80
|
+
subject.shutdown
|
81
|
+
sleep(0.1)
|
82
|
+
|
83
|
+
subject << :foo
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'calls #on_start when the thread is first started' do
|
87
|
+
actor = subject.instance_variable_get(:@actor)
|
88
|
+
actor.should_receive(:on_start).once.with(no_args)
|
89
|
+
subject << :foo
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'calls #on_reset when the thread is started after the first time' do
|
93
|
+
actor = subject.instance_variable_get(:@actor)
|
94
|
+
actor.should_receive(:on_reset).once.with(no_args)
|
95
|
+
subject << :terminate
|
96
|
+
sleep(0.1)
|
97
|
+
subject << :foo
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'abort_on_exception' do
|
102
|
+
|
103
|
+
after(:each) do
|
104
|
+
@ref.shutdown if @ref
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'gets set on the actor thread' do
|
108
|
+
@ref = shared_actor_test_class.spawn(abort_on_exception: true)
|
109
|
+
@ref << :foo
|
110
|
+
sleep(0.1)
|
111
|
+
@ref.instance_variable_get(:@thread).abort_on_exception.should be_true
|
112
|
+
|
113
|
+
@ref = shared_actor_test_class.spawn(abort_on_exception: false)
|
114
|
+
@ref << :foo
|
115
|
+
sleep(0.1)
|
116
|
+
@ref.instance_variable_get(:@thread).abort_on_exception.should be_false
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'defaults to true' do
|
120
|
+
@ref = shared_actor_test_class.spawn
|
121
|
+
@ref << :foo
|
122
|
+
sleep(0.1)
|
123
|
+
@ref.instance_variable_get(:@thread).abort_on_exception.should be_true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'reset_on_error' do
|
128
|
+
|
129
|
+
after(:each) do
|
130
|
+
@ref.shutdown if @ref
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'causes #on_reset to be called on exception when true' do
|
134
|
+
@ref = shared_actor_test_class.spawn(reset_on_error: true)
|
135
|
+
actor = @ref.instance_variable_get(:@actor)
|
136
|
+
actor.should_receive(:on_reset).once.with(no_args)
|
137
|
+
@ref << :poison
|
138
|
+
sleep(0.1)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'prevents #on_reset form being called on exception when false' do
|
142
|
+
@ref = shared_actor_test_class.spawn(reset_on_error: false)
|
143
|
+
actor = @ref.instance_variable_get(:@actor)
|
144
|
+
actor.should_not_receive(:on_reset).with(any_args)
|
145
|
+
@ref << :poison
|
146
|
+
sleep(0.1)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'defaults to true' do
|
150
|
+
@ref = shared_actor_test_class.spawn
|
151
|
+
actor = @ref.instance_variable_get(:@actor)
|
152
|
+
actor.should_receive(:on_reset).once.with(no_args)
|
153
|
+
@ref << :poison
|
154
|
+
sleep(0.1)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'rescue_exception' do
|
159
|
+
|
160
|
+
after(:each) do
|
161
|
+
@ref.shutdown if @ref
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'rescues Exception in the actor thread when true' do
|
165
|
+
@ref = shared_actor_test_class.spawn(
|
166
|
+
abort_on_exception: false,
|
167
|
+
rescue_exception: true
|
168
|
+
)
|
169
|
+
|
170
|
+
ivar = @ref.post(:poison)
|
171
|
+
sleep(0.1)
|
172
|
+
ivar.reason.should be_a StandardError
|
173
|
+
|
174
|
+
ivar = @ref.post(:bullet)
|
175
|
+
sleep(0.1)
|
176
|
+
ivar.reason.should be_a Exception
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'rescues StandardError in the actor thread when false' do
|
180
|
+
@ref = shared_actor_test_class.spawn(
|
181
|
+
abort_on_exception: false,
|
182
|
+
rescue_exception: false
|
183
|
+
)
|
184
|
+
|
185
|
+
ivar = @ref.post(:poison)
|
186
|
+
sleep(0.1)
|
187
|
+
ivar.reason.should be_a StandardError
|
188
|
+
|
189
|
+
ivar = @ref.post(:bullet)
|
190
|
+
sleep(0.1)
|
191
|
+
ivar.reason.should be_nil
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'defaults to false' do
|
195
|
+
@ref = shared_actor_test_class.spawn(abort_on_exception: false)
|
196
|
+
|
197
|
+
ivar = @ref.post(:poison)
|
198
|
+
sleep(0.1)
|
199
|
+
ivar.reason.should be_a StandardError
|
200
|
+
|
201
|
+
ivar = @ref.post(:bullet)
|
202
|
+
sleep(0.1)
|
203
|
+
ivar.reason.should be_nil
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context '#shutdown' do
|
208
|
+
|
209
|
+
it 'calls #on_shutdown when shutdown' do
|
210
|
+
actor = subject.instance_variable_get(:@actor)
|
211
|
+
actor.should_receive(:on_shutdown).once.with(no_args)
|
212
|
+
subject << :foo
|
213
|
+
sleep(0.1)
|
214
|
+
|
215
|
+
subject.shutdown
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe ThreadPoolExecutor do
|
6
|
+
if jruby?
|
7
|
+
it 'inherits from JavaThreadPoolExecutor' do
|
8
|
+
ThreadPoolExecutor.ancestors.should include(JavaThreadPoolExecutor)
|
9
|
+
end
|
10
|
+
else
|
11
|
+
it 'inherits from RubyThreadPoolExecutor' do
|
12
|
+
ThreadPoolExecutor.ancestors.should include(RubyThreadPoolExecutor)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe CachedThreadPool do
|
18
|
+
if jruby?
|
19
|
+
it 'inherits from JavaCachedThreadPool' do
|
20
|
+
CachedThreadPool.ancestors.should include(JavaCachedThreadPool)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
it 'inherits from RubyCachedThreadPool' do
|
24
|
+
CachedThreadPool.ancestors.should include(RubyCachedThreadPool)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe FixedThreadPool do
|
30
|
+
if jruby?
|
31
|
+
it 'inherits from JavaFixedThreadPool' do
|
32
|
+
FixedThreadPool.ancestors.should include(JavaFixedThreadPool)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
it 'inherits from RubyFixedThreadPool' do
|
36
|
+
FixedThreadPool.ancestors.should include(RubyFixedThreadPool)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'thread_pool_shared'
|
3
|
+
|
4
|
+
share_examples_for :thread_pool_executor do
|
5
|
+
|
6
|
+
after(:each) do
|
7
|
+
subject.kill
|
8
|
+
sleep(0.1)
|
9
|
+
end
|
10
|
+
|
11
|
+
it_should_behave_like :thread_pool
|
12
|
+
|
13
|
+
context '#initialize' do
|
14
|
+
|
15
|
+
it 'defaults :min_length to DEFAULT_MIN_POOL_SIZE' do
|
16
|
+
subject = described_class.new
|
17
|
+
subject.min_length.should eq described_class::DEFAULT_MIN_POOL_SIZE
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'defaults :max_length to DEFAULT_MAX_POOL_SIZE' do
|
21
|
+
subject = described_class.new
|
22
|
+
subject.max_length.should eq described_class::DEFAULT_MAX_POOL_SIZE
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'defaults :idletime to DEFAULT_THREAD_IDLETIMEOUT' do
|
26
|
+
subject = described_class.new
|
27
|
+
subject.idletime.should eq described_class::DEFAULT_THREAD_IDLETIMEOUT
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'defaults :max_queue to DEFAULT_MAX_QUEUE_SIZE' do
|
31
|
+
subject = described_class.new
|
32
|
+
subject.max_queue.should eq described_class::DEFAULT_MAX_QUEUE_SIZE
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'accepts all valid overflow policies' do
|
36
|
+
Concurrent::RubyThreadPoolExecutor::OVERFLOW_POLICIES.each do |policy|
|
37
|
+
subject = described_class.new(overflow_policy: policy)
|
38
|
+
subject.overflow_policy.should eq policy
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'defaults :overflow_policy to :abort' do
|
43
|
+
subject = described_class.new
|
44
|
+
subject.overflow_policy.should eq :abort
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'raises an exception if :min_threads is less than zero' do
|
48
|
+
expect {
|
49
|
+
described_class.new(min_threads: -1)
|
50
|
+
}.to raise_error(ArgumentError)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'raises an exception if :max_threads is not greater than zero' do
|
54
|
+
expect {
|
55
|
+
described_class.new(max_threads: 0)
|
56
|
+
}.to raise_error(ArgumentError)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'raises an exception if given an invalid :overflow_policy' do
|
60
|
+
expect {
|
61
|
+
described_class.new(overflow_policy: :bogus)
|
62
|
+
}.to raise_error(ArgumentError)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context '#max_queue' do
|
67
|
+
|
68
|
+
let!(:expected_max){ 100 }
|
69
|
+
subject{ described_class.new(max_queue: expected_max) }
|
70
|
+
|
71
|
+
it 'returns the set value on creation' do
|
72
|
+
subject.max_queue.should eq expected_max
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'returns the set value when running' do
|
76
|
+
5.times{ subject.post{ sleep(0.1) } }
|
77
|
+
sleep(0.1)
|
78
|
+
subject.max_queue.should eq expected_max
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns the set value after stopping' do
|
82
|
+
5.times{ subject.post{ sleep(0.1) } }
|
83
|
+
sleep(0.1)
|
84
|
+
subject.shutdown
|
85
|
+
subject.wait_for_termination(1)
|
86
|
+
subject.max_queue.should eq expected_max
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context '#queue_length' do
|
91
|
+
|
92
|
+
let!(:expected_max){ 10 }
|
93
|
+
subject do
|
94
|
+
described_class.new(
|
95
|
+
min_threads: 2,
|
96
|
+
max_threads: 5,
|
97
|
+
max_queue: expected_max,
|
98
|
+
overflow_policy: :discard
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'returns zero on creation' do
|
103
|
+
subject.queue_length.should eq 0
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'returns zero when there are no enqueued tasks' do
|
107
|
+
5.times{ subject.post{ nil } }
|
108
|
+
sleep(0.1)
|
109
|
+
subject.queue_length.should eq 0
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'returns the size of the queue when tasks are enqueued' do
|
113
|
+
100.times{ subject.post{ sleep(0.5) } }
|
114
|
+
sleep(0.1)
|
115
|
+
subject.queue_length.should > 0
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'returns zero when stopped' do
|
119
|
+
100.times{ subject.post{ sleep(0.5) } }
|
120
|
+
sleep(0.1)
|
121
|
+
subject.shutdown
|
122
|
+
subject.wait_for_termination(1)
|
123
|
+
subject.queue_length.should eq 0
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'can never be greater than :max_queue' do
|
127
|
+
100.times{ subject.post{ sleep(0.5) } }
|
128
|
+
sleep(0.1)
|
129
|
+
subject.queue_length.should <= expected_max
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context '#remaining_capacity' do
|
134
|
+
|
135
|
+
let!(:expected_max){ 100 }
|
136
|
+
subject{ described_class.new(max_queue: expected_max) }
|
137
|
+
|
138
|
+
it 'returns -1 when :max_queue is set to zero' do
|
139
|
+
executor = described_class.new(max_queue: 0)
|
140
|
+
executor.remaining_capacity.should eq -1
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'returns :max_length on creation' do
|
144
|
+
subject.remaining_capacity.should eq expected_max
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'returns :max_length when stopped' do
|
148
|
+
100.times{ subject.post{ nil } }
|
149
|
+
sleep(0.1)
|
150
|
+
subject.shutdown
|
151
|
+
subject.wait_for_termination(1)
|
152
|
+
subject.remaining_capacity.should eq expected_max
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require_relative 'global_thread_pool_shared'
|
2
3
|
|
3
4
|
share_examples_for :thread_pool do
|
4
5
|
|
@@ -7,6 +8,79 @@ share_examples_for :thread_pool do
|
|
7
8
|
sleep(0.1)
|
8
9
|
end
|
9
10
|
|
11
|
+
it_should_behave_like :global_thread_pool
|
12
|
+
|
13
|
+
context '#scheduled_task_count' do
|
14
|
+
|
15
|
+
it 'returns zero on creation' do
|
16
|
+
subject.scheduled_task_count.should eq 0
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns the approximate number of tasks that have been post thus far' do
|
20
|
+
10.times{ subject.post{ nil } }
|
21
|
+
sleep(0.1)
|
22
|
+
subject.scheduled_task_count.should > 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns the approximate number of tasks that were post' do
|
26
|
+
10.times{ subject.post{ nil } }
|
27
|
+
sleep(0.1)
|
28
|
+
subject.shutdown
|
29
|
+
subject.wait_for_termination(1)
|
30
|
+
subject.scheduled_task_count.should > 0
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context '#completed_task_count' do
|
35
|
+
|
36
|
+
it 'returns zero on creation' do
|
37
|
+
subject.completed_task_count.should eq 0
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns the approximate number of tasks that have been completed thus far' do
|
41
|
+
5.times{ subject.post{ raise StandardError } }
|
42
|
+
5.times{ subject.post{ nil } }
|
43
|
+
sleep(0.1)
|
44
|
+
subject.completed_task_count.should > 0
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns the approximate number of tasks that were completed' do
|
48
|
+
5.times{ subject.post{ raise StandardError } }
|
49
|
+
5.times{ subject.post{ nil } }
|
50
|
+
sleep(0.1)
|
51
|
+
subject.shutdown
|
52
|
+
subject.wait_for_termination(1)
|
53
|
+
subject.completed_task_count.should > 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context '#length' do
|
58
|
+
|
59
|
+
it 'returns zero on creation' do
|
60
|
+
subject.length.should eq 0
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'returns a non-zero while shutting down' do
|
64
|
+
5.times{ subject.post{ sleep(0.1) } }
|
65
|
+
subject.shutdown
|
66
|
+
subject.length.should > 0
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns zero once shut down' do
|
70
|
+
5.times{ subject.post{ sleep(0.1) } }
|
71
|
+
sleep(0.1)
|
72
|
+
subject.shutdown
|
73
|
+
subject.wait_for_termination(1)
|
74
|
+
subject.length.should eq 0
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'aliased as #current_length' do
|
78
|
+
5.times{ subject.post{ sleep(0.1) } }
|
79
|
+
sleep(0.1)
|
80
|
+
subject.current_length.should eq subject.length
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
10
84
|
context '#running?' do
|
11
85
|
|
12
86
|
it 'returns true when the thread pool is running' do
|
@@ -16,16 +90,19 @@ share_examples_for :thread_pool do
|
|
16
90
|
it 'returns false when the thread pool is shutting down' do
|
17
91
|
subject.post{ sleep(1) }
|
18
92
|
subject.shutdown
|
93
|
+
subject.wait_for_termination(1)
|
19
94
|
subject.should_not be_running
|
20
95
|
end
|
21
96
|
|
22
97
|
it 'returns false when the thread pool is shutdown' do
|
23
98
|
subject.shutdown
|
99
|
+
subject.wait_for_termination(1)
|
24
100
|
subject.should_not be_running
|
25
101
|
end
|
26
102
|
|
27
103
|
it 'returns false when the thread pool is killed' do
|
28
|
-
subject.
|
104
|
+
subject.kill
|
105
|
+
subject.wait_for_termination(1)
|
29
106
|
subject.should_not be_running
|
30
107
|
end
|
31
108
|
end
|
@@ -83,55 +160,40 @@ share_examples_for :thread_pool do
|
|
83
160
|
@expected.should be_false
|
84
161
|
end
|
85
162
|
|
86
|
-
it 'attempts to kill all in-progress tasks' do
|
87
|
-
@expected = false
|
88
|
-
subject.post{ sleep(1); @expected = true }
|
89
|
-
sleep(0.1)
|
90
|
-
subject.kill
|
91
|
-
sleep(1)
|
92
|
-
@expected.should be_false
|
93
|
-
end
|
94
|
-
|
95
163
|
it 'rejects all pending tasks' do
|
96
|
-
|
97
|
-
subject.post{ sleep(0.5) }
|
98
|
-
subject.post{ sleep(0.5); @expected = true }
|
164
|
+
subject.post{ sleep(1) }
|
99
165
|
sleep(0.1)
|
100
166
|
subject.kill
|
101
|
-
sleep(1)
|
102
|
-
|
167
|
+
sleep(0.1)
|
168
|
+
subject.post{ nil }.should be_false
|
103
169
|
end
|
104
170
|
|
105
171
|
it 'kills all threads' do
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
172
|
+
unless jruby? # this check is incorrect, want to check for class
|
173
|
+
pending('brittle test--counting threads is not reliable')
|
174
|
+
before_thread_count = Thread.list.size
|
175
|
+
100.times { subject << proc{ sleep(1) } }
|
176
|
+
sleep(0.1)
|
177
|
+
Thread.list.size.should > before_thread_count
|
178
|
+
subject.kill
|
179
|
+
sleep(0.1)
|
180
|
+
Thread.list.size.should == before_thread_count
|
181
|
+
end
|
113
182
|
end
|
114
183
|
end
|
115
184
|
|
116
185
|
context '#wait_for_termination' do
|
117
186
|
|
118
|
-
it 'immediately returns true when no
|
187
|
+
it 'immediately returns true when no operations are pending' do
|
119
188
|
subject.shutdown
|
120
|
-
subject.wait_for_termination.should be_true
|
189
|
+
subject.wait_for_termination(0).should be_true
|
121
190
|
end
|
122
191
|
|
123
192
|
it 'returns true after shutdown has complete' do
|
124
|
-
10.times { subject << proc{
|
125
|
-
sleep(0.1)
|
126
|
-
subject.shutdown
|
127
|
-
subject.wait_for_termination.should be_true
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'blocks indefinitely when timeout is nil' do
|
131
|
-
subject.post{ sleep(1) }
|
193
|
+
10.times { subject << proc{ nil } }
|
132
194
|
sleep(0.1)
|
133
195
|
subject.shutdown
|
134
|
-
subject.wait_for_termination(
|
196
|
+
subject.wait_for_termination(1).should be_true
|
135
197
|
end
|
136
198
|
|
137
199
|
it 'returns true when shutdown sucessfully completes before timeout' do
|
@@ -142,10 +204,10 @@ share_examples_for :thread_pool do
|
|
142
204
|
end
|
143
205
|
|
144
206
|
it 'returns false when shutdown fails to complete before timeout' do
|
145
|
-
subject.post{ sleep }
|
207
|
+
(subject.length + 10).times{ subject.post{ sleep(1) } }
|
146
208
|
sleep(0.1)
|
147
209
|
subject.shutdown
|
148
|
-
subject.wait_for_termination(
|
210
|
+
subject.wait_for_termination(0).should be_false
|
149
211
|
end
|
150
212
|
end
|
151
213
|
|
@@ -158,7 +220,7 @@ share_examples_for :thread_pool do
|
|
158
220
|
end
|
159
221
|
|
160
222
|
it 'returns true when the block is added to the queue' do
|
161
|
-
subject.post{
|
223
|
+
subject.post{ nil }.should be_true
|
162
224
|
end
|
163
225
|
|
164
226
|
it 'calls the block with the given arguments' do
|