concurrent-ruby 0.6.0.pre.1 → 0.6.0.pre.2
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 +16 -0
- data/lib/concurrent.rb +9 -29
- data/lib/concurrent/{actor.rb → actor/actor.rb} +3 -3
- data/lib/concurrent/actor/actor_context.rb +77 -0
- data/lib/concurrent/actor/actor_ref.rb +67 -0
- data/lib/concurrent/{postable.rb → actor/postable.rb} +1 -1
- data/lib/concurrent/actor/simple_actor_ref.rb +94 -0
- data/lib/concurrent/actors.rb +5 -0
- data/lib/concurrent/agent.rb +81 -47
- data/lib/concurrent/async.rb +35 -35
- data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +170 -0
- data/lib/concurrent/{condition.rb → atomic/condition.rb} +0 -0
- data/lib/concurrent/{copy_on_notify_observer_set.rb → atomic/copy_on_notify_observer_set.rb} +48 -13
- data/lib/concurrent/{copy_on_write_observer_set.rb → atomic/copy_on_write_observer_set.rb} +41 -20
- data/lib/concurrent/atomic/count_down_latch.rb +116 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
- data/lib/concurrent/atomic/event.rb +103 -0
- data/lib/concurrent/{thread_local_var.rb → atomic/thread_local_var.rb} +0 -0
- data/lib/concurrent/atomics.rb +9 -0
- data/lib/concurrent/channel/buffered_channel.rb +6 -4
- data/lib/concurrent/channel/channel.rb +30 -2
- data/lib/concurrent/channel/unbuffered_channel.rb +2 -2
- data/lib/concurrent/channel/waitable_list.rb +3 -1
- data/lib/concurrent/channels.rb +5 -0
- data/lib/concurrent/{channel → collection}/blocking_ring_buffer.rb +16 -5
- data/lib/concurrent/collection/priority_queue.rb +305 -0
- data/lib/concurrent/{channel → collection}/ring_buffer.rb +6 -1
- data/lib/concurrent/collections.rb +3 -0
- data/lib/concurrent/configuration.rb +68 -19
- data/lib/concurrent/dataflow.rb +9 -9
- data/lib/concurrent/delay.rb +21 -13
- data/lib/concurrent/dereferenceable.rb +40 -33
- data/lib/concurrent/exchanger.rb +3 -0
- data/lib/concurrent/{cached_thread_pool.rb → executor/cached_thread_pool.rb} +8 -9
- data/lib/concurrent/executor/executor.rb +222 -0
- data/lib/concurrent/{fixed_thread_pool.rb → executor/fixed_thread_pool.rb} +6 -7
- data/lib/concurrent/{immediate_executor.rb → executor/immediate_executor.rb} +5 -5
- data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
- data/lib/concurrent/{java_fixed_thread_pool.rb → executor/java_fixed_thread_pool.rb} +7 -11
- data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
- data/lib/concurrent/{java_thread_pool_executor.rb → executor/java_thread_pool_executor.rb} +66 -77
- data/lib/concurrent/executor/one_by_one.rb +65 -0
- data/lib/concurrent/{per_thread_executor.rb → executor/per_thread_executor.rb} +4 -4
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
- data/lib/concurrent/{ruby_fixed_thread_pool.rb → executor/ruby_fixed_thread_pool.rb} +5 -4
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +72 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +282 -0
- data/lib/concurrent/{ruby_thread_pool_worker.rb → executor/ruby_thread_pool_worker.rb} +6 -6
- data/lib/concurrent/{safe_task_executor.rb → executor/safe_task_executor.rb} +20 -13
- data/lib/concurrent/executor/single_thread_executor.rb +35 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
- data/lib/concurrent/executor/timer_set.rb +138 -0
- data/lib/concurrent/executors.rb +9 -0
- data/lib/concurrent/future.rb +39 -40
- data/lib/concurrent/ivar.rb +22 -15
- data/lib/concurrent/mvar.rb +2 -1
- data/lib/concurrent/obligation.rb +9 -3
- data/lib/concurrent/observable.rb +33 -0
- data/lib/concurrent/options_parser.rb +46 -0
- data/lib/concurrent/promise.rb +23 -24
- data/lib/concurrent/scheduled_task.rb +21 -45
- data/lib/concurrent/timer_task.rb +204 -126
- data/lib/concurrent/tvar.rb +1 -1
- data/lib/concurrent/utilities.rb +3 -36
- data/lib/concurrent/{processor_count.rb → utility/processor_count.rb} +1 -1
- data/lib/concurrent/utility/timeout.rb +36 -0
- data/lib/concurrent/utility/timer.rb +21 -0
- data/lib/concurrent/version.rb +1 -1
- data/lib/concurrent_ruby_ext.bundle +0 -0
- data/spec/concurrent/{actor_context_spec.rb → actor/actor_context_spec.rb} +0 -8
- data/spec/concurrent/{actor_ref_shared.rb → actor/actor_ref_shared.rb} +9 -59
- data/spec/concurrent/{actor_spec.rb → actor/actor_spec.rb} +43 -41
- data/spec/concurrent/{postable_shared.rb → actor/postable_shared.rb} +0 -0
- data/spec/concurrent/actor/simple_actor_ref_spec.rb +135 -0
- data/spec/concurrent/agent_spec.rb +160 -71
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +172 -0
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +186 -0
- data/spec/concurrent/{condition_spec.rb → atomic/condition_spec.rb} +2 -2
- data/spec/concurrent/{copy_on_notify_observer_set_spec.rb → atomic/copy_on_notify_observer_set_spec.rb} +0 -0
- data/spec/concurrent/{copy_on_write_observer_set_spec.rb → atomic/copy_on_write_observer_set_spec.rb} +0 -0
- data/spec/concurrent/atomic/count_down_latch_spec.rb +151 -0
- data/spec/concurrent/atomic/cyclic_barrier_spec.rb +248 -0
- data/spec/concurrent/{event_spec.rb → atomic/event_spec.rb} +18 -3
- data/spec/concurrent/{observer_set_shared.rb → atomic/observer_set_shared.rb} +15 -6
- data/spec/concurrent/{thread_local_var_spec.rb → atomic/thread_local_var_spec.rb} +0 -0
- data/spec/concurrent/channel/buffered_channel_spec.rb +1 -1
- data/spec/concurrent/channel/channel_spec.rb +6 -4
- data/spec/concurrent/channel/probe_spec.rb +37 -9
- data/spec/concurrent/channel/unbuffered_channel_spec.rb +2 -2
- data/spec/concurrent/{channel → collection}/blocking_ring_buffer_spec.rb +0 -0
- data/spec/concurrent/collection/priority_queue_spec.rb +317 -0
- data/spec/concurrent/{channel → collection}/ring_buffer_spec.rb +0 -0
- data/spec/concurrent/configuration_spec.rb +4 -70
- data/spec/concurrent/dereferenceable_shared.rb +5 -4
- data/spec/concurrent/exchanger_spec.rb +10 -5
- data/spec/concurrent/{cached_thread_pool_shared.rb → executor/cached_thread_pool_shared.rb} +15 -37
- data/spec/concurrent/{fixed_thread_pool_shared.rb → executor/fixed_thread_pool_shared.rb} +0 -0
- data/spec/concurrent/{global_thread_pool_shared.rb → executor/global_thread_pool_shared.rb} +10 -8
- data/spec/concurrent/{immediate_executor_spec.rb → executor/immediate_executor_spec.rb} +0 -0
- data/spec/concurrent/{java_cached_thread_pool_spec.rb → executor/java_cached_thread_pool_spec.rb} +1 -21
- data/spec/concurrent/{java_fixed_thread_pool_spec.rb → executor/java_fixed_thread_pool_spec.rb} +0 -0
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +21 -0
- data/spec/concurrent/{java_thread_pool_executor_spec.rb → executor/java_thread_pool_executor_spec.rb} +0 -0
- data/spec/concurrent/{per_thread_executor_spec.rb → executor/per_thread_executor_spec.rb} +0 -4
- data/spec/concurrent/{ruby_cached_thread_pool_spec.rb → executor/ruby_cached_thread_pool_spec.rb} +1 -1
- data/spec/concurrent/{ruby_fixed_thread_pool_spec.rb → executor/ruby_fixed_thread_pool_spec.rb} +0 -0
- data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +18 -0
- data/spec/concurrent/{ruby_thread_pool_executor_spec.rb → executor/ruby_thread_pool_executor_spec.rb} +12 -24
- data/spec/concurrent/executor/safe_task_executor_spec.rb +103 -0
- data/spec/concurrent/{thread_pool_class_cast_spec.rb → executor/thread_pool_class_cast_spec.rb} +12 -0
- data/spec/concurrent/{thread_pool_executor_shared.rb → executor/thread_pool_executor_shared.rb} +0 -0
- data/spec/concurrent/{thread_pool_shared.rb → executor/thread_pool_shared.rb} +84 -119
- data/spec/concurrent/executor/timer_set_spec.rb +183 -0
- data/spec/concurrent/future_spec.rb +12 -0
- data/spec/concurrent/ivar_spec.rb +11 -1
- data/spec/concurrent/observable_shared.rb +173 -0
- data/spec/concurrent/observable_spec.rb +51 -0
- data/spec/concurrent/options_parser_spec.rb +71 -0
- data/spec/concurrent/runnable_shared.rb +6 -0
- data/spec/concurrent/scheduled_task_spec.rb +60 -40
- data/spec/concurrent/timer_task_spec.rb +130 -144
- data/spec/concurrent/{processor_count_spec.rb → utility/processor_count_spec.rb} +0 -0
- data/spec/concurrent/{utilities_spec.rb → utility/timeout_spec.rb} +0 -0
- data/spec/concurrent/utility/timer_spec.rb +52 -0
- metadata +147 -108
- data/lib/concurrent/actor_context.rb +0 -31
- data/lib/concurrent/actor_ref.rb +0 -39
- data/lib/concurrent/atomic.rb +0 -121
- data/lib/concurrent/channel/probe.rb +0 -19
- data/lib/concurrent/count_down_latch.rb +0 -60
- data/lib/concurrent/event.rb +0 -80
- data/lib/concurrent/java_cached_thread_pool.rb +0 -45
- data/lib/concurrent/ruby_cached_thread_pool.rb +0 -37
- data/lib/concurrent/ruby_thread_pool_executor.rb +0 -268
- data/lib/concurrent/simple_actor_ref.rb +0 -124
- data/lib/concurrent/thread_pool_executor.rb +0 -30
- data/spec/concurrent/atomic_spec.rb +0 -201
- data/spec/concurrent/count_down_latch_spec.rb +0 -125
- data/spec/concurrent/safe_task_executor_spec.rb +0 -58
- data/spec/concurrent/simple_actor_ref_spec.rb +0 -219
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require_relative 'dereferenceable_shared'
|
3
3
|
require_relative 'obligation_shared'
|
4
|
+
require_relative 'observable_shared'
|
4
5
|
|
5
6
|
module Concurrent
|
6
7
|
|
@@ -54,6 +55,17 @@ module Concurrent
|
|
54
55
|
end
|
55
56
|
|
56
57
|
it_should_behave_like :dereferenceable
|
58
|
+
|
59
|
+
# observable
|
60
|
+
|
61
|
+
subject{ Future.new{ nil } }
|
62
|
+
|
63
|
+
def trigger_observable(observable)
|
64
|
+
observable.execute
|
65
|
+
sleep(0.1)
|
66
|
+
end
|
67
|
+
|
68
|
+
it_should_behave_like :observable
|
57
69
|
end
|
58
70
|
|
59
71
|
context 'subclassing' do
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require_relative 'dereferenceable_shared'
|
3
3
|
require_relative 'obligation_shared'
|
4
|
+
require_relative 'observable_shared'
|
4
5
|
|
5
6
|
module Concurrent
|
6
7
|
|
@@ -56,10 +57,19 @@ module Concurrent
|
|
56
57
|
|
57
58
|
def execute_dereferenceable(subject)
|
58
59
|
subject.set('value')
|
59
|
-
sleep(0.1)
|
60
60
|
end
|
61
61
|
|
62
62
|
it_should_behave_like :dereferenceable
|
63
|
+
|
64
|
+
# observable
|
65
|
+
|
66
|
+
subject{ IVar.new }
|
67
|
+
|
68
|
+
def trigger_observable(observable)
|
69
|
+
observable.set('value')
|
70
|
+
end
|
71
|
+
|
72
|
+
it_should_behave_like :observable
|
63
73
|
end
|
64
74
|
|
65
75
|
context '#initialize' do
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
share_examples_for :observable do
|
4
|
+
|
5
|
+
let(:observer_set) do
|
6
|
+
subject.instance_variable_get(:@observers)
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:observer_class) do
|
10
|
+
Class.new do
|
11
|
+
def initialize(&block)
|
12
|
+
@block = block
|
13
|
+
end
|
14
|
+
def update(*args)
|
15
|
+
@block.call(*args) if @block
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:observer){ observer_class.new }
|
21
|
+
|
22
|
+
let!(:observer_func){ :notify }
|
23
|
+
|
24
|
+
let(:observer_with_func_class) do
|
25
|
+
Class.new do
|
26
|
+
def initialize(&block)
|
27
|
+
@block = block
|
28
|
+
end
|
29
|
+
def notify(*args)
|
30
|
+
@block.call(*args) if @block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:observer_with_func){ observer_with_func_class.new }
|
36
|
+
|
37
|
+
context '#add_observer' do
|
38
|
+
|
39
|
+
it 'adds an observer if called before first notification' do
|
40
|
+
observer_set.should_receive(:add_observer).with(any_args)
|
41
|
+
subject.add_observer(observer)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'adds an observer with :func if called before first notification' do
|
45
|
+
observer_set.should_receive(:add_observer).with(observer_with_func, :notify)
|
46
|
+
subject.add_observer(observer_with_func, observer_func)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'creates an observer from a block if called before first notification' do
|
50
|
+
block = proc{ nil }
|
51
|
+
observer_set.should_receive(:add_observer).with(any_args)
|
52
|
+
subject.add_observer(&block)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'raises an exception if not given an observer or a block' do
|
56
|
+
expect {
|
57
|
+
subject.add_observer
|
58
|
+
}.to raise_error(ArgumentError)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'raises an exception when given both an observer and a block' do
|
62
|
+
expect {
|
63
|
+
subject.add_observer(observer){ nil }
|
64
|
+
}.to raise_error(ArgumentError)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context '#delete_observer' do
|
69
|
+
|
70
|
+
it 'deletes the given observer if called before first notification' do
|
71
|
+
subject.count_observers.should eq 0
|
72
|
+
subject.add_observer(observer)
|
73
|
+
subject.count_observers.should eq 1
|
74
|
+
subject.delete_observer(observer)
|
75
|
+
subject.count_observers.should eq 0
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'returns the removed observer if found in the observer set' do
|
79
|
+
subject.add_observer(observer)
|
80
|
+
subject.delete_observer(observer).should eq observer
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'returns the given observer even when not found in the observer set' do
|
84
|
+
subject.delete_observer(observer).should eq observer
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context '#delete_observers' do
|
89
|
+
|
90
|
+
it 'deletes all observers when called before first notification' do
|
91
|
+
5.times{ subject.add_observer(observer_class.new) }
|
92
|
+
subject.count_observers.should eq 5
|
93
|
+
subject.delete_observers
|
94
|
+
subject.count_observers.should eq 0
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'returns self' do
|
98
|
+
subject.delete_observers.should eq subject
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context '#count_observers' do
|
103
|
+
|
104
|
+
it 'returns zero for a new observable object' do
|
105
|
+
subject.count_observers.should eq 0
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'returns a count of registered observers if called before first notification' do
|
109
|
+
5.times{ subject.add_observer(observer_class.new) }
|
110
|
+
subject.count_observers.should eq 5
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'returns zero after #delete_observers has been called' do
|
114
|
+
5.times{ subject.add_observer(observer_class.new) }
|
115
|
+
subject.delete_observers
|
116
|
+
subject.count_observers.should eq 0
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'first notification' do
|
121
|
+
|
122
|
+
it 'calls the #update method on all observers without a specified :func' do
|
123
|
+
latch = Concurrent::CountDownLatch.new(5)
|
124
|
+
5.times do
|
125
|
+
subject.add_observer(observer_class.new{ latch.count_down })
|
126
|
+
end
|
127
|
+
trigger_observable(subject)
|
128
|
+
latch.count.should eq 0
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'calls the appropriate function on all observers which specified a :func' do
|
132
|
+
latch = Concurrent::CountDownLatch.new(5)
|
133
|
+
5.times do
|
134
|
+
obs = observer_with_func_class.new{ latch.count_down }
|
135
|
+
subject.add_observer(obs, observer_func)
|
136
|
+
end
|
137
|
+
trigger_observable(subject)
|
138
|
+
latch.count.should eq 0
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'calls the proc for all observers added as a block' do
|
142
|
+
latch = Concurrent::CountDownLatch.new(5)
|
143
|
+
5.times do
|
144
|
+
subject.add_observer{ latch.count_down }
|
145
|
+
end
|
146
|
+
trigger_observable(subject)
|
147
|
+
latch.count.should eq 0
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'does not notify any observers removed with #delete_observer' do
|
151
|
+
latch = Concurrent::CountDownLatch.new(5)
|
152
|
+
|
153
|
+
obs = observer_class.new{ latch.count_down }
|
154
|
+
subject.add_observer(obs)
|
155
|
+
subject.delete_observer(obs)
|
156
|
+
|
157
|
+
trigger_observable(subject)
|
158
|
+
latch.count.should eq 5
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'does not notify any observers after #delete_observers called' do
|
162
|
+
latch = Concurrent::CountDownLatch.new(5)
|
163
|
+
5.times do
|
164
|
+
subject.add_observer(observer_class.new{ latch.count_down })
|
165
|
+
end
|
166
|
+
|
167
|
+
subject.delete_observers
|
168
|
+
|
169
|
+
trigger_observable(subject)
|
170
|
+
latch.count.should eq 5
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe Observable do
|
6
|
+
|
7
|
+
let (:described_class) do
|
8
|
+
Class.new do
|
9
|
+
include Concurrent::Observable
|
10
|
+
public :observers, :observers=
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:observer_set) { double(:observer_set) }
|
15
|
+
subject { described_class.new }
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
subject.observers = observer_set
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'does not initialize set by by default' do
|
22
|
+
described_class.new.observers.should be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'uses the given observer set' do
|
26
|
+
expected = CopyOnWriteObserverSet.new
|
27
|
+
subject.observers = expected
|
28
|
+
subject.observers.should eql expected
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'delegates #add_observer' do
|
32
|
+
observer_set.should_receive(:add_observer).with(:observer)
|
33
|
+
subject.add_observer(:observer)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'delegates #delete_observer' do
|
37
|
+
observer_set.should_receive(:delete_observer).with(:observer)
|
38
|
+
subject.delete_observer(:observer)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'delegates #delete_observers' do
|
42
|
+
observer_set.should_receive(:delete_observers).with(no_args)
|
43
|
+
subject.delete_observers
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'delegates #count_observers' do
|
47
|
+
observer_set.should_receive(:count_observers).with(no_args)
|
48
|
+
subject.count_observers
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe OptionsParser do
|
6
|
+
|
7
|
+
let(:executor){ ImmediateExecutor.new }
|
8
|
+
|
9
|
+
let(:task_pool){ ImmediateExecutor.new }
|
10
|
+
let(:operation_pool){ ImmediateExecutor.new }
|
11
|
+
|
12
|
+
context '#get_executor_from' do
|
13
|
+
|
14
|
+
it 'returns the given :executor' do
|
15
|
+
OptionsParser::get_executor_from(executor: executor).should eq executor
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns the global operation pool when :operation is true' do
|
19
|
+
Concurrent.configuration.should_receive(:global_operation_pool).
|
20
|
+
and_return(:operation_pool)
|
21
|
+
OptionsParser::get_executor_from(operation: true)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns the global task pool when :operation is false' do
|
25
|
+
Concurrent.configuration.should_receive(:global_task_pool).
|
26
|
+
and_return(:task_pool)
|
27
|
+
OptionsParser::get_executor_from(operation: false)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns the global operation pool when :task is false' do
|
31
|
+
Concurrent.configuration.should_receive(:global_operation_pool).
|
32
|
+
and_return(:operation_pool)
|
33
|
+
OptionsParser::get_executor_from(task: false)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns the global task pool when :task is true' do
|
37
|
+
Concurrent.configuration.should_receive(:global_task_pool).
|
38
|
+
and_return(:task_pool)
|
39
|
+
OptionsParser::get_executor_from(task: true)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns the global task pool when :executor is nil' do
|
43
|
+
Concurrent.configuration.should_receive(:global_task_pool).
|
44
|
+
and_return(:task_pool)
|
45
|
+
OptionsParser::get_executor_from(executor: nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns the global task pool when no option is given' do
|
49
|
+
Concurrent.configuration.should_receive(:global_task_pool).
|
50
|
+
and_return(:task_pool)
|
51
|
+
OptionsParser::get_executor_from
|
52
|
+
end
|
53
|
+
|
54
|
+
specify ':executor overrides :operation' do
|
55
|
+
OptionsParser::get_executor_from(executor: executor, operation: true).
|
56
|
+
should eq executor
|
57
|
+
end
|
58
|
+
|
59
|
+
specify ':executor overrides :task' do
|
60
|
+
OptionsParser::get_executor_from(executor: executor, task: true).
|
61
|
+
should eq executor
|
62
|
+
end
|
63
|
+
|
64
|
+
specify ':operation overrides :task' do
|
65
|
+
Concurrent.configuration.should_receive(:global_operation_pool).
|
66
|
+
and_return(:operation_pool)
|
67
|
+
OptionsParser::get_executor_from(operation: true, task: true)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -55,7 +55,13 @@ share_examples_for :runnable do
|
|
55
55
|
subject.should be_running
|
56
56
|
end
|
57
57
|
|
58
|
+
it 'returns false when first created' do
|
59
|
+
subject.should_not be_running
|
60
|
+
end
|
61
|
+
|
58
62
|
it 'returns false when not running' do
|
63
|
+
subject.stop
|
64
|
+
sleep(0.1)
|
59
65
|
subject.should_not be_running
|
60
66
|
end
|
61
67
|
end
|
@@ -2,6 +2,7 @@ require 'spec_helper'
|
|
2
2
|
require 'timecop'
|
3
3
|
require_relative 'dereferenceable_shared'
|
4
4
|
require_relative 'obligation_shared'
|
5
|
+
require_relative 'observable_shared'
|
5
6
|
|
6
7
|
module Concurrent
|
7
8
|
|
@@ -19,11 +20,19 @@ module Concurrent
|
|
19
20
|
end
|
20
21
|
|
21
22
|
let(:fulfilled_subject) do
|
22
|
-
|
23
|
+
latch = Concurrent::CountDownLatch.new(1)
|
24
|
+
task = ScheduledTask.new(0.1){ latch.count_down; fulfilled_value }.execute
|
25
|
+
latch.wait(1)
|
26
|
+
sleep(0.1)
|
27
|
+
task
|
23
28
|
end
|
24
29
|
|
25
30
|
let(:rejected_subject) do
|
26
|
-
|
31
|
+
latch = Concurrent::CountDownLatch.new(1)
|
32
|
+
task = ScheduledTask.new(0.1){ latch.count_down; raise rejected_reason }.execute
|
33
|
+
latch.wait(1)
|
34
|
+
sleep(0.1)
|
35
|
+
task
|
27
36
|
end
|
28
37
|
|
29
38
|
it_should_behave_like :obligation
|
@@ -44,6 +53,17 @@ module Concurrent
|
|
44
53
|
end
|
45
54
|
|
46
55
|
it_should_behave_like :dereferenceable
|
56
|
+
|
57
|
+
# observable
|
58
|
+
|
59
|
+
subject{ ScheduledTask.new(0.1){ nil } }
|
60
|
+
|
61
|
+
def trigger_observable(observable)
|
62
|
+
observable.execute
|
63
|
+
sleep(0.2)
|
64
|
+
end
|
65
|
+
|
66
|
+
it_should_behave_like :observable
|
47
67
|
end
|
48
68
|
|
49
69
|
context '#initialize' do
|
@@ -65,19 +85,19 @@ module Concurrent
|
|
65
85
|
it 'raises an exception when seconds is less than zero' do
|
66
86
|
expect {
|
67
87
|
ScheduledTask.new(-1){ nil }
|
68
|
-
}.to raise_error(
|
88
|
+
}.to raise_error(ArgumentError)
|
69
89
|
end
|
70
90
|
|
71
91
|
it 'raises an exception when schedule time is in the past' do
|
72
92
|
expect {
|
73
93
|
ScheduledTask.new(Time.now - 60){ nil }
|
74
|
-
}.to raise_error(
|
94
|
+
}.to raise_error(ArgumentError)
|
75
95
|
end
|
76
96
|
|
77
97
|
it 'raises an exception when no block given' do
|
78
98
|
expect {
|
79
99
|
ScheduledTask.new(1)
|
80
|
-
}.to raise_error(
|
100
|
+
}.to raise_error(ArgumentError)
|
81
101
|
end
|
82
102
|
|
83
103
|
it 'sets the initial state to :unscheduled' do
|
@@ -121,16 +141,10 @@ module Concurrent
|
|
121
141
|
Timecop.travel(60)
|
122
142
|
expect {
|
123
143
|
task.execute
|
124
|
-
}.to raise_error(
|
144
|
+
}.to raise_error(ArgumentError)
|
125
145
|
end
|
126
146
|
end
|
127
147
|
|
128
|
-
it 'spawns a new thread when a block was given on construction' do
|
129
|
-
Thread.should_receive(:new).with(any_args)
|
130
|
-
task = ScheduledTask.new(1){ nil }
|
131
|
-
task.execute
|
132
|
-
end
|
133
|
-
|
134
148
|
it 'sets the sate to :pending' do
|
135
149
|
task = ScheduledTask.new(1){ nil }
|
136
150
|
task.execute
|
@@ -153,7 +167,7 @@ module Concurrent
|
|
153
167
|
it 'passes the block to the new ScheduledTask' do
|
154
168
|
@expected = false
|
155
169
|
task = ScheduledTask.execute(0.1){ @expected = true }
|
156
|
-
|
170
|
+
task.value(1)
|
157
171
|
@expected.should be_true
|
158
172
|
end
|
159
173
|
|
@@ -169,37 +183,39 @@ module Concurrent
|
|
169
183
|
|
170
184
|
it 'returns false if the task has already been performed' do
|
171
185
|
task = ScheduledTask.new(0.1){ 42 }.execute
|
172
|
-
|
186
|
+
task.value(1)
|
173
187
|
task.cancel.should be_false
|
174
188
|
end
|
175
189
|
|
176
190
|
it 'returns false if the task is already in progress' do
|
177
|
-
|
178
|
-
|
191
|
+
latch = Concurrent::CountDownLatch.new(1)
|
192
|
+
task = ScheduledTask.new(0.1) {
|
193
|
+
latch.count_down
|
194
|
+
sleep(1)
|
195
|
+
}.execute
|
196
|
+
latch.wait(1)
|
179
197
|
task.cancel.should be_false
|
180
198
|
end
|
181
199
|
|
182
200
|
it 'cancels the task if it has not yet scheduled' do
|
183
|
-
|
184
|
-
task = ScheduledTask.new(0.1){
|
201
|
+
latch = Concurrent::CountDownLatch.new(1)
|
202
|
+
task = ScheduledTask.new(0.1){ latch.count_down }
|
185
203
|
task.cancel
|
186
204
|
task.execute
|
187
|
-
|
188
|
-
@expected.should be_true
|
205
|
+
latch.wait(0.3).should be_false
|
189
206
|
end
|
190
207
|
|
191
208
|
|
192
209
|
it 'cancels the task if it has not yet started' do
|
193
|
-
|
194
|
-
task = ScheduledTask.new(0.3){
|
210
|
+
latch = Concurrent::CountDownLatch.new(1)
|
211
|
+
task = ScheduledTask.new(0.3){ latch.count_down }.execute
|
195
212
|
sleep(0.1)
|
196
213
|
task.cancel
|
197
|
-
|
198
|
-
@expected.should be_true
|
214
|
+
latch.wait(0.5).should be_false
|
199
215
|
end
|
200
216
|
|
201
217
|
it 'returns true on success' do
|
202
|
-
task = ScheduledTask.new(
|
218
|
+
task = ScheduledTask.new(10){ nil }.execute
|
203
219
|
sleep(0.1)
|
204
220
|
task.cancel.should be_true
|
205
221
|
end
|
@@ -215,8 +231,12 @@ module Concurrent
|
|
215
231
|
context 'execution' do
|
216
232
|
|
217
233
|
it 'sets the state to :in_progress when the task is running' do
|
218
|
-
|
219
|
-
|
234
|
+
latch = Concurrent::CountDownLatch.new(1)
|
235
|
+
task = ScheduledTask.new(0.1) {
|
236
|
+
latch.count_down
|
237
|
+
sleep(1)
|
238
|
+
}.execute
|
239
|
+
latch.wait(1)
|
220
240
|
task.should be_in_progress
|
221
241
|
end
|
222
242
|
end
|
@@ -228,10 +248,15 @@ module Concurrent
|
|
228
248
|
attr_reader :value
|
229
249
|
attr_reader :reason
|
230
250
|
attr_reader :count
|
231
|
-
|
251
|
+
attr_reader :latch
|
252
|
+
def initialize
|
253
|
+
@latch = Concurrent::CountDownLatch.new(1)
|
254
|
+
end
|
255
|
+
def update(time, value, reason)
|
232
256
|
@count = @count.to_i + 1
|
233
257
|
@value = value
|
234
258
|
@reason = reason
|
259
|
+
@latch.count_down
|
235
260
|
end
|
236
261
|
end
|
237
262
|
end
|
@@ -262,22 +287,20 @@ module Concurrent
|
|
262
287
|
|
263
288
|
it 'returns false for an observer added once :fulfilled' do
|
264
289
|
task = ScheduledTask.new(0.1){ 42 }.execute
|
265
|
-
|
290
|
+
task.value(1)
|
266
291
|
task.add_observer(observer).should be_false
|
267
292
|
end
|
268
293
|
|
269
294
|
it 'returns false for an observer added once :rejected' do
|
270
295
|
task = ScheduledTask.new(0.1){ raise StandardError }.execute
|
271
|
-
|
296
|
+
task.value(0.2)
|
272
297
|
task.add_observer(observer).should be_false
|
273
298
|
end
|
274
299
|
|
275
300
|
it 'notifies all observers on fulfillment' do
|
276
301
|
task = ScheduledTask.new(0.1){ 42 }.execute
|
277
302
|
task.add_observer(observer)
|
278
|
-
|
279
|
-
task.value.should == 42
|
280
|
-
task.reason.should be_nil
|
303
|
+
observer.latch.wait(1)
|
281
304
|
observer.value.should == 42
|
282
305
|
observer.reason.should be_nil
|
283
306
|
end
|
@@ -285,9 +308,7 @@ module Concurrent
|
|
285
308
|
it 'notifies all observers on rejection' do
|
286
309
|
task = ScheduledTask.new(0.1){ raise StandardError }.execute
|
287
310
|
task.add_observer(observer)
|
288
|
-
|
289
|
-
task.value.should be_nil
|
290
|
-
task.reason.should be_a(StandardError)
|
311
|
+
observer.latch.wait(1)
|
291
312
|
observer.value.should be_nil
|
292
313
|
observer.reason.should be_a(StandardError)
|
293
314
|
end
|
@@ -295,7 +316,7 @@ module Concurrent
|
|
295
316
|
it 'does not notify an observer added after fulfillment' do
|
296
317
|
observer.should_not_receive(:update).with(any_args)
|
297
318
|
task = ScheduledTask.new(0.1){ 42 }.execute
|
298
|
-
|
319
|
+
task.value(1)
|
299
320
|
task.add_observer(observer)
|
300
321
|
sleep(0.1)
|
301
322
|
end
|
@@ -303,7 +324,7 @@ module Concurrent
|
|
303
324
|
it 'does not notify an observer added after rejection' do
|
304
325
|
observer.should_not_receive(:update).with(any_args)
|
305
326
|
task = ScheduledTask.new(0.1){ raise StandardError }.execute
|
306
|
-
|
327
|
+
task.value(1)
|
307
328
|
task.add_observer(observer)
|
308
329
|
sleep(0.1)
|
309
330
|
end
|
@@ -313,9 +334,8 @@ module Concurrent
|
|
313
334
|
task = ScheduledTask.new(0.1){ 42 }.execute
|
314
335
|
task.cancel
|
315
336
|
task.add_observer(observer)
|
316
|
-
|
337
|
+
task.value(1)
|
317
338
|
end
|
318
|
-
|
319
339
|
end
|
320
340
|
end
|
321
341
|
end
|