concurrent-ruby 0.6.0 → 0.7.0.rc0
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 +55 -51
- data/lib/concurrent/actress/ad_hoc.rb +6 -0
- data/lib/concurrent/actress/context.rb +10 -8
- data/lib/concurrent/actress/core.rb +44 -20
- data/lib/concurrent/actress/core_delegations.rb +5 -0
- data/lib/concurrent/actress/envelope.rb +21 -5
- data/lib/concurrent/actress/reference.rb +5 -5
- data/lib/concurrent/actress.rb +152 -6
- data/lib/concurrent/agent.rb +9 -9
- 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 +7 -2
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +0 -1
- data/lib/concurrent/executor/{one_by_one.rb → serialized_execution.rb} +21 -8
- data/lib/concurrent/executor/timer_set.rb +6 -1
- data/lib/concurrent/executors.rb +1 -1
- data/lib/concurrent/logging.rb +1 -1
- data/lib/concurrent/observable.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.rb +3 -4
- data/lib/concurrent_ruby_ext.so +0 -0
- data/lib/extension_helper.rb +9 -0
- metadata +19 -152
- 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/actress/doc.md +0 -53
- 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 -191
- 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 -71
- 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 -239
- 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 -342
- data/spec/concurrent/stoppable_shared.rb +0 -37
- data/spec/concurrent/supervisor_spec.rb +0 -1148
- data/spec/concurrent/timer_task_spec.rb +0 -258
- 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 -31
- data/spec/support/example_group_extensions.rb +0 -48
- data/spec/support/less_than_or_equal_to_matcher.rb +0 -5
- /data/{LICENSE → LICENSE.txt} +0 -0
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
module Concurrent
|
|
4
|
-
|
|
5
|
-
describe 'dataflow' do
|
|
6
|
-
|
|
7
|
-
let(:executor) { ImmediateExecutor.new }
|
|
8
|
-
let(:root_executor) { PerThreadExecutor.new }
|
|
9
|
-
|
|
10
|
-
it 'raises an exception when no block given' do
|
|
11
|
-
expect { Concurrent::dataflow }.to raise_error(ArgumentError)
|
|
12
|
-
expect { Concurrent::dataflow_with(root_executor) }.to raise_error(ArgumentError)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
specify '#dataflow uses the global task pool' do
|
|
16
|
-
input = Future.execute{0}
|
|
17
|
-
Concurrent.should_receive(:dataflow_with).once.
|
|
18
|
-
with(Concurrent.configuration.global_task_pool, input)
|
|
19
|
-
Concurrent::dataflow(input){0}
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
specify '#dataflow_with uses the given executor' do
|
|
23
|
-
input = Future.execute{0}
|
|
24
|
-
result = Future.new{0}
|
|
25
|
-
|
|
26
|
-
Future.should_receive(:new).with(executor: root_executor).and_return(result)
|
|
27
|
-
Concurrent::dataflow_with(root_executor, input){0}
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
specify '#dataflow_with raises an exception when no executor given' do
|
|
31
|
-
expect {
|
|
32
|
-
Concurrent::dataflow_with(nil){ nil }
|
|
33
|
-
}.to raise_error(ArgumentError)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
it 'accepts zero or more dependencies' do
|
|
37
|
-
Concurrent::dataflow(){0}
|
|
38
|
-
Concurrent::dataflow(Future.execute{0}){0}
|
|
39
|
-
Concurrent::dataflow(Future.execute{0}, Future.execute{0}){0}
|
|
40
|
-
|
|
41
|
-
Concurrent::dataflow_with(root_executor, ){0}
|
|
42
|
-
Concurrent::dataflow_with(root_executor, Future.execute{0}){0}
|
|
43
|
-
Concurrent::dataflow_with(root_executor, Future.execute{0}, Future.execute{0}){0}
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it 'accepts uncompleted dependencies' do
|
|
47
|
-
d = Future.new(executor: executor){0}
|
|
48
|
-
Concurrent::dataflow(d){0}
|
|
49
|
-
d.execute
|
|
50
|
-
|
|
51
|
-
d = Future.new(executor: executor){0}
|
|
52
|
-
Concurrent::dataflow_with(root_executor, d){0}
|
|
53
|
-
d.execute
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it 'accepts completed dependencies' do
|
|
57
|
-
d = Future.new(executor: executor){0}
|
|
58
|
-
d.execute
|
|
59
|
-
Concurrent::dataflow(d){0}
|
|
60
|
-
|
|
61
|
-
d = Future.new(executor: executor){0}
|
|
62
|
-
d.execute
|
|
63
|
-
Concurrent::dataflow_with(root_executor, d){0}
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
it 'raises an exception if any dependencies are not IVars' do
|
|
67
|
-
expect { Concurrent::dataflow(nil) }.to raise_error(ArgumentError)
|
|
68
|
-
expect { Concurrent::dataflow(Future.execute{0}, nil) }.to raise_error(ArgumentError)
|
|
69
|
-
expect { Concurrent::dataflow(nil, Future.execute{0}) }.to raise_error(ArgumentError)
|
|
70
|
-
|
|
71
|
-
expect { Concurrent::dataflow_with(root_executor, nil) }.to raise_error(ArgumentError)
|
|
72
|
-
expect { Concurrent::dataflow_with(root_executor, Future.execute{0}, nil) }.to raise_error(ArgumentError)
|
|
73
|
-
expect { Concurrent::dataflow_with(root_executor, nil, Future.execute{0}) }.to raise_error(ArgumentError)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it 'returns a Future' do
|
|
77
|
-
Concurrent::dataflow{0}.should be_a(Future)
|
|
78
|
-
Concurrent::dataflow{0}.should be_a(Future)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
context 'does not schedule the Future' do
|
|
82
|
-
|
|
83
|
-
specify 'if no dependencies are completed' do
|
|
84
|
-
d = Future.new(executor: executor){0}
|
|
85
|
-
f = Concurrent::dataflow(d){0}
|
|
86
|
-
f.should be_unscheduled
|
|
87
|
-
d.execute
|
|
88
|
-
|
|
89
|
-
d = Future.new(executor: executor){0}
|
|
90
|
-
f = Concurrent::dataflow_with(root_executor, d){0}
|
|
91
|
-
f.should be_unscheduled
|
|
92
|
-
d.execute
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
specify 'if one dependency of two is completed' do
|
|
96
|
-
d1 = Future.new(executor: executor){0}
|
|
97
|
-
d2 = Future.new(executor: executor){0}
|
|
98
|
-
f = Concurrent::dataflow(d1, d2){0}
|
|
99
|
-
d1.execute
|
|
100
|
-
f.should be_unscheduled
|
|
101
|
-
d2.execute
|
|
102
|
-
|
|
103
|
-
d1 = Future.new(executor: executor){0}
|
|
104
|
-
d2 = Future.new(executor: executor){0}
|
|
105
|
-
f = Concurrent::dataflow_with(root_executor, d1, d2){0}
|
|
106
|
-
d1.execute
|
|
107
|
-
f.should be_unscheduled
|
|
108
|
-
d2.execute
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
context 'schedules the Future when all dependencies are available' do
|
|
113
|
-
|
|
114
|
-
specify 'if there is just one' do
|
|
115
|
-
d = Future.new(executor: executor){0}
|
|
116
|
-
f = Concurrent::dataflow(d){0}
|
|
117
|
-
d.execute
|
|
118
|
-
f.value.should eq 0
|
|
119
|
-
|
|
120
|
-
d = Future.new(executor: executor){0}
|
|
121
|
-
f = Concurrent::dataflow_with(root_executor, d){0}
|
|
122
|
-
d.execute
|
|
123
|
-
f.value.should eq 0
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
specify 'if there is more than one' do
|
|
127
|
-
d1 = Future.new(executor: executor){0}
|
|
128
|
-
d2 = Future.new(executor: executor){0}
|
|
129
|
-
f = Concurrent::dataflow(d1, d2){0}
|
|
130
|
-
d1.execute
|
|
131
|
-
d2.execute
|
|
132
|
-
f.value.should eq 0
|
|
133
|
-
|
|
134
|
-
d1 = Future.new(executor: executor){0}
|
|
135
|
-
d2 = Future.new(executor: executor){0}
|
|
136
|
-
f = Concurrent::dataflow_with(root_executor, d1, d2){0}
|
|
137
|
-
d1.execute
|
|
138
|
-
d2.execute
|
|
139
|
-
f.value.should eq 0
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
context 'counts already executed dependencies' do
|
|
144
|
-
|
|
145
|
-
specify 'if there is just one' do
|
|
146
|
-
d = Future.new(executor: executor){0}
|
|
147
|
-
d.execute
|
|
148
|
-
f = Concurrent::dataflow(d){0}
|
|
149
|
-
f.value.should eq 0
|
|
150
|
-
|
|
151
|
-
d = Future.new(executor: executor){0}
|
|
152
|
-
d.execute
|
|
153
|
-
f = Concurrent::dataflow_with(root_executor, d){0}
|
|
154
|
-
f.value.should eq 0
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
specify 'if there is more than one' do
|
|
158
|
-
d1 = Future.new(executor: executor){0}
|
|
159
|
-
d2 = Future.new(executor: executor){0}
|
|
160
|
-
d1.execute
|
|
161
|
-
d2.execute
|
|
162
|
-
f = Concurrent::dataflow(d1, d2){0}
|
|
163
|
-
f.value.should eq 0
|
|
164
|
-
|
|
165
|
-
d1 = Future.new(executor: executor){0}
|
|
166
|
-
d2 = Future.new(executor: executor){0}
|
|
167
|
-
d1.execute
|
|
168
|
-
d2.execute
|
|
169
|
-
f = Concurrent::dataflow_with(root_executor, d1, d2){0}
|
|
170
|
-
f.value.should eq 0
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
context 'passes the values of dependencies into the block' do
|
|
175
|
-
|
|
176
|
-
specify 'if there is just one' do
|
|
177
|
-
d = Future.new(executor: executor){14}
|
|
178
|
-
f = Concurrent::dataflow(d){|v| v }
|
|
179
|
-
d.execute
|
|
180
|
-
f.value.should eq 14
|
|
181
|
-
|
|
182
|
-
d = Future.new(executor: executor){14}
|
|
183
|
-
f = Concurrent::dataflow_with(root_executor, d){|v| v }
|
|
184
|
-
d.execute
|
|
185
|
-
f.value.should eq 14
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
specify 'if there is more than one' do
|
|
189
|
-
d1 = Future.new(executor: executor){14}
|
|
190
|
-
d2 = Future.new(executor: executor){2}
|
|
191
|
-
f = Concurrent::dataflow(d1, d2) {|v1, v2| v1 + v2}
|
|
192
|
-
d1.execute
|
|
193
|
-
d2.execute
|
|
194
|
-
f.value.should eq 16
|
|
195
|
-
|
|
196
|
-
d1 = Future.new(executor: executor){14}
|
|
197
|
-
d2 = Future.new(executor: executor){2}
|
|
198
|
-
f = Concurrent::dataflow_with(root_executor, d1, d2) {|v1, v2| v1 + v2}
|
|
199
|
-
d1.execute
|
|
200
|
-
d2.execute
|
|
201
|
-
f.value.should eq 16
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
context 'module function' do
|
|
206
|
-
|
|
207
|
-
it 'can be called as Concurrent.dataflow and Concurrent.dataflow_with' do
|
|
208
|
-
|
|
209
|
-
def fib_with_dot(n)
|
|
210
|
-
if n < 2
|
|
211
|
-
Concurrent.dataflow { n }
|
|
212
|
-
else
|
|
213
|
-
n1 = fib_with_dot(n - 1)
|
|
214
|
-
n2 = fib_with_dot(n - 2)
|
|
215
|
-
Concurrent.dataflow_with(root_executor, n1, n2) { n1.value + n2.value }
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
expected = fib_with_dot(14)
|
|
220
|
-
sleep(0.1)
|
|
221
|
-
expected.value.should eq 377
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
it 'can be called as Concurrent::dataflow and Concurrent::dataflow_with' do
|
|
225
|
-
|
|
226
|
-
def fib_with_colons(n)
|
|
227
|
-
if n < 2
|
|
228
|
-
Concurrent::dataflow { n }
|
|
229
|
-
else
|
|
230
|
-
n1 = fib_with_colons(n - 1)
|
|
231
|
-
n2 = fib_with_colons(n - 2)
|
|
232
|
-
Concurrent::dataflow_with(root_executor, n1, n2) { n1.value + n2.value }
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
expected = fib_with_colons(14)
|
|
237
|
-
sleep(0.1)
|
|
238
|
-
expected.value.should eq 377
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
end
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require_relative 'dereferenceable_shared'
|
|
3
|
-
require_relative 'obligation_shared'
|
|
4
|
-
|
|
5
|
-
module Concurrent
|
|
6
|
-
|
|
7
|
-
describe Delay do
|
|
8
|
-
|
|
9
|
-
context 'behavior' do
|
|
10
|
-
|
|
11
|
-
# dereferenceable
|
|
12
|
-
|
|
13
|
-
def dereferenceable_subject(value, opts = {})
|
|
14
|
-
delay = Delay.new(opts){ value }
|
|
15
|
-
delay.tap{ delay.value }
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it_should_behave_like :dereferenceable
|
|
19
|
-
|
|
20
|
-
# obligation
|
|
21
|
-
|
|
22
|
-
let!(:fulfilled_value) { 10 }
|
|
23
|
-
let!(:rejected_reason) { StandardError.new('mojo jojo') }
|
|
24
|
-
|
|
25
|
-
let(:pending_subject) do
|
|
26
|
-
Delay.new{ fulfilled_value }
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
let(:fulfilled_subject) do
|
|
30
|
-
delay = Delay.new{ fulfilled_value }
|
|
31
|
-
delay.tap{ delay.value }
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
let(:rejected_subject) do
|
|
35
|
-
delay = Delay.new{ raise rejected_reason }
|
|
36
|
-
delay.tap{ delay.value }
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it_should_behave_like :obligation
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
context '#initialize' do
|
|
43
|
-
|
|
44
|
-
it 'sets the state to :pending' do
|
|
45
|
-
Delay.new{ nil }.state.should eq :pending
|
|
46
|
-
Delay.new{ nil }.should be_pending
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it 'raises an exception when no block given' do
|
|
50
|
-
expect {
|
|
51
|
-
Delay.new
|
|
52
|
-
}.to raise_error(ArgumentError)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
context '#reconfigure' do
|
|
58
|
-
it 'returns value of block used in reconfiguration' do
|
|
59
|
-
Delay.new { nil }.tap { |d| d.reconfigure { true } }.value.should be_true
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it 'returns false when process completed?' do
|
|
63
|
-
d = Delay.new { 1 }
|
|
64
|
-
d.reconfigure { 2 }.should be_true
|
|
65
|
-
d.value.should be 2
|
|
66
|
-
d.reconfigure { 3 }.should be_false
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
context '#value' do
|
|
71
|
-
|
|
72
|
-
let(:task){ proc{ nil } }
|
|
73
|
-
|
|
74
|
-
it 'does not call the block before #value is called' do
|
|
75
|
-
task.should_not_receive(:call).with(any_args)
|
|
76
|
-
Delay.new(&task)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
it 'calls the block when #value is called' do
|
|
80
|
-
task.should_receive(:call).once.with(any_args).and_return(nil)
|
|
81
|
-
Delay.new(&task).value
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
it 'only calls the block once no matter how often #value is called' do
|
|
85
|
-
task.should_receive(:call).once.with(any_args).and_return(nil)
|
|
86
|
-
delay = Delay.new(&task)
|
|
87
|
-
5.times{ delay.value }
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
share_examples_for :dereferenceable do
|
|
4
|
-
|
|
5
|
-
it 'defaults :dup_on_deref to false' do
|
|
6
|
-
value = 'value'
|
|
7
|
-
value.should_not_receive(:dup).with(any_args)
|
|
8
|
-
|
|
9
|
-
subject = dereferenceable_subject(value)
|
|
10
|
-
subject.value.should eq 'value'
|
|
11
|
-
|
|
12
|
-
subject = dereferenceable_subject(value, dup_on_deref: false)
|
|
13
|
-
subject.value.should eq 'value'
|
|
14
|
-
|
|
15
|
-
subject = dereferenceable_subject(value, dup: false)
|
|
16
|
-
subject.value.should eq 'value'
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it 'calls #dup when the :dup_on_deref option is true' do
|
|
20
|
-
value = 'value'
|
|
21
|
-
|
|
22
|
-
subject = dereferenceable_subject(value, dup_on_deref: true)
|
|
23
|
-
subject.value.object_id.should_not eq value.object_id
|
|
24
|
-
subject.value.should eq 'value'
|
|
25
|
-
|
|
26
|
-
subject = dereferenceable_subject(value, dup: true)
|
|
27
|
-
subject.value.object_id.should_not eq value.object_id
|
|
28
|
-
subject.value.should eq 'value'
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it 'defaults :freeze_on_deref to false' do
|
|
32
|
-
value = 'value'
|
|
33
|
-
value.should_not_receive(:freeze).with(any_args)
|
|
34
|
-
|
|
35
|
-
subject = dereferenceable_subject(value)
|
|
36
|
-
subject.value.should eq 'value'
|
|
37
|
-
|
|
38
|
-
subject = dereferenceable_subject(value, freeze_on_deref: false)
|
|
39
|
-
subject.value.should eq 'value'
|
|
40
|
-
|
|
41
|
-
subject = dereferenceable_subject(value, freeze: false)
|
|
42
|
-
subject.value.should eq 'value'
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
it 'calls #freeze when the :freeze_on_deref option is true' do
|
|
46
|
-
value = 'value'
|
|
47
|
-
|
|
48
|
-
subject = dereferenceable_subject(value, freeze_on_deref: true)
|
|
49
|
-
subject.value.should be_frozen
|
|
50
|
-
subject.value.should eq 'value'
|
|
51
|
-
|
|
52
|
-
subject = dereferenceable_subject(value, freeze: true)
|
|
53
|
-
subject.value.should be_frozen
|
|
54
|
-
subject.value.should eq 'value'
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
it 'defaults :copy_on_deref to nil' do
|
|
58
|
-
value = 'value'
|
|
59
|
-
|
|
60
|
-
subject = dereferenceable_subject(value)
|
|
61
|
-
subject.value.object_id.should == value.object_id
|
|
62
|
-
subject.value.should eq 'value'
|
|
63
|
-
|
|
64
|
-
subject = dereferenceable_subject(value, copy_on_deref: nil)
|
|
65
|
-
subject.value.object_id.should == value.object_id
|
|
66
|
-
subject.value.should eq 'value'
|
|
67
|
-
|
|
68
|
-
subject = dereferenceable_subject(value, copy: nil)
|
|
69
|
-
subject.value.object_id.should == value.object_id
|
|
70
|
-
subject.value.should eq 'value'
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
it 'calls the block when the :copy_on_deref option is passed a proc' do
|
|
74
|
-
value = 'value'
|
|
75
|
-
copy = proc{|val| 'copy' }
|
|
76
|
-
|
|
77
|
-
subject = dereferenceable_subject(value, copy_on_deref: copy)
|
|
78
|
-
subject.value.object_id.should_not == value.object_id
|
|
79
|
-
|
|
80
|
-
subject = dereferenceable_subject(value, copy: copy)
|
|
81
|
-
subject.value.object_id.should_not == value.object_id
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
it 'calls the :copy block first followed by #dup followed by #freeze' do
|
|
85
|
-
value = 'value'
|
|
86
|
-
copied = 'copied'
|
|
87
|
-
dup = 'dup'
|
|
88
|
-
frozen = 'frozen'
|
|
89
|
-
copy = proc{|val| copied }
|
|
90
|
-
|
|
91
|
-
copied.should_receive(:dup).at_least(:once).with(no_args).and_return(dup)
|
|
92
|
-
dup.should_receive(:freeze).at_least(:once).with(no_args).and_return(frozen)
|
|
93
|
-
|
|
94
|
-
subject = dereferenceable_subject(value, dup_on_deref: true, freeze_on_deref: true, copy_on_deref: copy)
|
|
95
|
-
subject.value.should eq frozen
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
it 'does not call #dup when #dup_on_deref is set and the value is nil' do
|
|
99
|
-
allow_message_expectations_on_nil
|
|
100
|
-
result = nil
|
|
101
|
-
result.should_not_receive(:dup).with(any_args)
|
|
102
|
-
subject = dereferenceable_subject(result, dup_on_deref: true)
|
|
103
|
-
subject.value
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
it 'does not call #freeze when #freeze_on_deref is set and the value is nil' do
|
|
107
|
-
allow_message_expectations_on_nil
|
|
108
|
-
result = nil
|
|
109
|
-
result.should_not_receive(:freeze).with(any_args)
|
|
110
|
-
subject = dereferenceable_subject(result, freeze_on_deref: true)
|
|
111
|
-
subject.value
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
it 'does not call the #copy_on_deref block when the value is nil' do
|
|
115
|
-
copier = proc { 42 }
|
|
116
|
-
subject = dereferenceable_subject(nil, copy_on_deref: copier)
|
|
117
|
-
subject.value.should be_nil
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
it 'locks when all options are false' do
|
|
121
|
-
subject = dereferenceable_subject(0)
|
|
122
|
-
mutex = double('mutex')
|
|
123
|
-
subject.stub(:mutex).and_return(mutex)
|
|
124
|
-
mutex.should_receive(:lock).at_least(:once)
|
|
125
|
-
mutex.should_receive(:unlock).at_least(:once)
|
|
126
|
-
subject.value
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
it 'supports dereference flags with observers' do
|
|
130
|
-
if dereferenceable_subject(0).respond_to?(:add_observer)
|
|
131
|
-
|
|
132
|
-
result = 'result'
|
|
133
|
-
result.should_receive(:dup).at_least(:once).and_return(result)
|
|
134
|
-
result.should_receive(:freeze).at_least(:once).and_return(result)
|
|
135
|
-
copier = proc { result }
|
|
136
|
-
|
|
137
|
-
observer = double('observer')
|
|
138
|
-
observer.should_receive(:update).at_least(:once).with(any_args)
|
|
139
|
-
|
|
140
|
-
subject = dereferenceable_observable(dup_on_deref: true, freeze_on_deref: true, copy_on_deref: copier)
|
|
141
|
-
|
|
142
|
-
subject.add_observer(observer)
|
|
143
|
-
execute_dereferenceable(subject)
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
end
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
module Concurrent
|
|
4
|
-
|
|
5
|
-
describe Exchanger do
|
|
6
|
-
|
|
7
|
-
subject { Exchanger.new }
|
|
8
|
-
let!(:exchanger) { subject } # let is not thread safe, let! creates the object before ensuring uniqueness
|
|
9
|
-
|
|
10
|
-
describe 'exchange' do
|
|
11
|
-
context 'without timeout' do
|
|
12
|
-
it 'should block' do
|
|
13
|
-
t = Thread.new { exchanger.exchange(1) }
|
|
14
|
-
sleep(0.1)
|
|
15
|
-
t.status.should eq 'sleep'
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it 'should receive the other value' do
|
|
19
|
-
first_value = nil
|
|
20
|
-
second_value = nil
|
|
21
|
-
|
|
22
|
-
Thread.new { first_value = exchanger.exchange(2) }
|
|
23
|
-
Thread.new { second_value = exchanger.exchange(4) }
|
|
24
|
-
|
|
25
|
-
sleep(0.1)
|
|
26
|
-
|
|
27
|
-
first_value.should eq 4
|
|
28
|
-
second_value.should eq 2
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it 'can be reused' do
|
|
32
|
-
first_value = nil
|
|
33
|
-
second_value = nil
|
|
34
|
-
|
|
35
|
-
Thread.new { first_value = exchanger.exchange(1) }
|
|
36
|
-
Thread.new { second_value = exchanger.exchange(0) }
|
|
37
|
-
|
|
38
|
-
sleep(0.1)
|
|
39
|
-
|
|
40
|
-
Thread.new { first_value = exchanger.exchange(10) }
|
|
41
|
-
Thread.new { second_value = exchanger.exchange(12) }
|
|
42
|
-
|
|
43
|
-
sleep(0.1)
|
|
44
|
-
|
|
45
|
-
first_value.should eq 12
|
|
46
|
-
second_value.should eq 10
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
context 'with timeout' do
|
|
51
|
-
it 'should block until timeout' do
|
|
52
|
-
|
|
53
|
-
value = 0
|
|
54
|
-
start = Time.now.to_f
|
|
55
|
-
|
|
56
|
-
future = Concurrent::Future.execute do
|
|
57
|
-
exchanger.exchange(2, 0.2)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
future.value.should be_nil
|
|
61
|
-
(Time.now.to_f - start).should >= 0.2
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require_relative 'thread_pool_shared'
|
|
3
|
-
|
|
4
|
-
share_examples_for :cached_thread_pool do
|
|
5
|
-
|
|
6
|
-
subject do
|
|
7
|
-
described_class.new(overflow_policy: :discard)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
after(:each) do
|
|
11
|
-
subject.kill
|
|
12
|
-
sleep(0.1)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it_should_behave_like :thread_pool
|
|
16
|
-
|
|
17
|
-
context '#initialize' do
|
|
18
|
-
|
|
19
|
-
it 'sets :max_length to DEFAULT_MAX_POOL_SIZE' do
|
|
20
|
-
described_class.new.max_length.should eq described_class::DEFAULT_MAX_POOL_SIZE
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it 'sets :min_length to DEFAULT_MIN_POOL_SIZE' do
|
|
24
|
-
subject = described_class.new.min_length.should eq described_class::DEFAULT_MIN_POOL_SIZE
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it 'sets :idletime to DEFAULT_THREAD_IDLETIMEOUT' do
|
|
28
|
-
subject = described_class.new.idletime.should eq described_class::DEFAULT_THREAD_IDLETIMEOUT
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it 'sets :max_queue to DEFAULT_MAX_QUEUE_SIZE' do
|
|
32
|
-
subject = described_class.new.max_queue.should eq described_class::DEFAULT_MAX_QUEUE_SIZE
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
context '#min_length' do
|
|
37
|
-
|
|
38
|
-
it 'returns zero on creation' do
|
|
39
|
-
subject.min_length.should eq 0
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it 'returns zero while running' do
|
|
43
|
-
10.times{ subject.post{ nil } }
|
|
44
|
-
sleep(0.1)
|
|
45
|
-
subject.min_length.should eq 0
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
it 'returns zero once shutdown' do
|
|
49
|
-
10.times{ subject.post{ nil } }
|
|
50
|
-
sleep(0.1)
|
|
51
|
-
subject.shutdown
|
|
52
|
-
subject.wait_for_termination(1)
|
|
53
|
-
subject.min_length.should eq 0
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
context '#max_length' do
|
|
58
|
-
|
|
59
|
-
it 'returns :max_length on creation' do
|
|
60
|
-
subject.max_length.should eq described_class::DEFAULT_MAX_POOL_SIZE
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
it 'returns :max_length while running' do
|
|
64
|
-
10.times{ subject.post{ nil } }
|
|
65
|
-
sleep(0.1)
|
|
66
|
-
subject.max_length.should eq described_class::DEFAULT_MAX_POOL_SIZE
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it 'returns :max_length once shutdown' do
|
|
70
|
-
10.times{ subject.post{ nil } }
|
|
71
|
-
sleep(0.1)
|
|
72
|
-
subject.shutdown
|
|
73
|
-
subject.wait_for_termination(1)
|
|
74
|
-
subject.max_length.should eq described_class::DEFAULT_MAX_POOL_SIZE
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
context '#largest_length' do
|
|
79
|
-
|
|
80
|
-
it 'returns zero on creation' do
|
|
81
|
-
subject.largest_length.should eq 0
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
it 'returns a non-zero number once tasks have been received' do
|
|
85
|
-
10.times{ subject.post{ sleep(0.1) } }
|
|
86
|
-
sleep(0.1)
|
|
87
|
-
subject.largest_length.should > 0
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
it 'returns a non-zero number after shutdown if tasks have been received' do
|
|
91
|
-
10.times{ subject.post{ sleep(0.1) } }
|
|
92
|
-
sleep(0.1)
|
|
93
|
-
subject.shutdown
|
|
94
|
-
subject.wait_for_termination(1)
|
|
95
|
-
subject.largest_length.should > 0
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
context '#status' do
|
|
100
|
-
|
|
101
|
-
it 'returns an array' do
|
|
102
|
-
subject.stub(:warn)
|
|
103
|
-
subject.status.should be_kind_of(Array)
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
context '#idletime' do
|
|
108
|
-
|
|
109
|
-
subject{ described_class.new(idletime: 42) }
|
|
110
|
-
|
|
111
|
-
it 'returns the thread idletime' do
|
|
112
|
-
subject.idletime.should eq described_class::DEFAULT_THREAD_IDLETIMEOUT
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|