concurrent-ruby 0.6.1 → 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 +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
@@ -1,183 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
describe TimerSet do
|
6
|
-
|
7
|
-
subject{ TimerSet.new(executor: ImmediateExecutor.new) }
|
8
|
-
|
9
|
-
after(:each){ subject.kill }
|
10
|
-
|
11
|
-
it 'uses the executor given at construction' do
|
12
|
-
executor = double(:executor)
|
13
|
-
executor.should_receive(:post).with(no_args)
|
14
|
-
subject = TimerSet.new(executor: executor)
|
15
|
-
subject.post(0){ nil }
|
16
|
-
sleep(0.1)
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'uses the global task pool be default' do
|
20
|
-
Concurrent.configuration.global_task_pool.should_receive(:post).with(no_args)
|
21
|
-
subject = TimerSet.new
|
22
|
-
subject.post(0){ nil }
|
23
|
-
sleep(0.1)
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'executes a given task when given a Time' do
|
27
|
-
latch = CountDownLatch.new(1)
|
28
|
-
subject.post(Time.now + 0.1){ latch.count_down }
|
29
|
-
latch.wait(0.2).should be_true
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'executes a given task when given an interval in seconds' do
|
33
|
-
latch = CountDownLatch.new(1)
|
34
|
-
subject.post(0.1){ latch.count_down }
|
35
|
-
latch.wait(0.2).should be_true
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'passes all arguments to the task on execution' do
|
39
|
-
expected = nil
|
40
|
-
latch = CountDownLatch.new(1)
|
41
|
-
subject.post(0.1, 1, 2, 3) do |*args|
|
42
|
-
expected = args
|
43
|
-
latch.count_down
|
44
|
-
end
|
45
|
-
latch.wait(0.2).should be_true
|
46
|
-
expected.should eq [1, 2, 3]
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'immediately posts a task when the delay is zero' do
|
50
|
-
Thread.should_not_receive(:new).with(any_args)
|
51
|
-
subject.post(0){ true }
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'does not execute tasks early' do
|
55
|
-
expected = AtomicFixnum.new(0)
|
56
|
-
subject.post(0.2){ expected.increment }
|
57
|
-
sleep(0.15)
|
58
|
-
expected.value.should eq 0
|
59
|
-
sleep(0.10)
|
60
|
-
expected.value.should eq 1
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'raises an exception when given a task with a past Time value' do
|
64
|
-
expect {
|
65
|
-
subject.post(Time.now - 10){ nil }
|
66
|
-
}.to raise_error(ArgumentError)
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'raises an exception when given a task with a delay less than zero' do
|
70
|
-
expect {
|
71
|
-
subject.post(-10){ nil }
|
72
|
-
}.to raise_error(ArgumentError)
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'raises an exception when no block given' do
|
76
|
-
expect {
|
77
|
-
subject.post(10)
|
78
|
-
}.to raise_error(ArgumentError)
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'executes all tasks scheduled for the same time' do
|
82
|
-
latch = CountDownLatch.new(5)
|
83
|
-
5.times{ subject.post(0.1){ latch.count_down } }
|
84
|
-
latch.wait(0.2).should be_true
|
85
|
-
end
|
86
|
-
|
87
|
-
it 'executes tasks with different times in schedule order' do
|
88
|
-
expected = []
|
89
|
-
3.times{|i| subject.post(i/10){ expected << i } }
|
90
|
-
sleep(0.3)
|
91
|
-
expected.should eq [0, 1, 2]
|
92
|
-
end
|
93
|
-
|
94
|
-
it 'cancels all pending tasks on #shutdown' do
|
95
|
-
expected = AtomicFixnum.new(0)
|
96
|
-
10.times{ subject.post(0.2){ expected.increment } }
|
97
|
-
sleep(0.1)
|
98
|
-
subject.shutdown
|
99
|
-
sleep(0.2)
|
100
|
-
expected.value.should eq 0
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'cancels all pending tasks on #kill' do
|
104
|
-
expected = AtomicFixnum.new(0)
|
105
|
-
10.times{ subject.post(0.2){ expected.increment } }
|
106
|
-
sleep(0.1)
|
107
|
-
subject.kill
|
108
|
-
sleep(0.2)
|
109
|
-
expected.value.should eq 0
|
110
|
-
end
|
111
|
-
|
112
|
-
it 'stops the monitor thread on #shutdown' do
|
113
|
-
timer_executor = subject.instance_variable_get(:@timer_executor)
|
114
|
-
subject.shutdown
|
115
|
-
sleep(0.1)
|
116
|
-
timer_executor.should_not be_running
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'kills the monitor thread on #kill' do
|
120
|
-
timer_executor = subject.instance_variable_get(:@timer_executor)
|
121
|
-
subject.kill
|
122
|
-
sleep(0.1)
|
123
|
-
timer_executor.should_not be_running
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'rejects tasks once shutdown' do
|
127
|
-
expected = AtomicFixnum.new(0)
|
128
|
-
subject.shutdown
|
129
|
-
sleep(0.1)
|
130
|
-
subject.post(0){ expected.increment }.should be_false
|
131
|
-
sleep(0.1)
|
132
|
-
expected.value.should eq 0
|
133
|
-
end
|
134
|
-
|
135
|
-
it 'rejects tasks once killed' do
|
136
|
-
expected = AtomicFixnum.new(0)
|
137
|
-
subject.kill
|
138
|
-
sleep(0.1)
|
139
|
-
subject.post(0){ expected.increment }.should be_false
|
140
|
-
sleep(0.1)
|
141
|
-
expected.value.should eq 0
|
142
|
-
end
|
143
|
-
|
144
|
-
it 'is running? when first created' do
|
145
|
-
subject.should be_running
|
146
|
-
subject.should_not be_shutdown
|
147
|
-
end
|
148
|
-
|
149
|
-
it 'is running? after tasks have been post' do
|
150
|
-
subject.post(0.1){ nil }
|
151
|
-
subject.should be_running
|
152
|
-
subject.should_not be_shutdown
|
153
|
-
end
|
154
|
-
|
155
|
-
it 'is shutdown? after shutdown completes' do
|
156
|
-
subject.shutdown
|
157
|
-
sleep(0.1)
|
158
|
-
subject.should_not be_running
|
159
|
-
subject.should be_shutdown
|
160
|
-
end
|
161
|
-
|
162
|
-
it 'is shutdown? after being killed' do
|
163
|
-
subject.kill
|
164
|
-
sleep(0.1)
|
165
|
-
subject.should_not be_running
|
166
|
-
subject.should be_shutdown
|
167
|
-
end
|
168
|
-
|
169
|
-
specify '#wait_for_termination returns true if shutdown completes before timeout' do
|
170
|
-
subject.post(0.1){ nil }
|
171
|
-
sleep(0.1)
|
172
|
-
subject.shutdown
|
173
|
-
subject.wait_for_termination(0.1).should be_true
|
174
|
-
end
|
175
|
-
|
176
|
-
specify '#wait_for_termination returns false on timeout' do
|
177
|
-
subject.post(0.1){ nil }
|
178
|
-
sleep(0.1)
|
179
|
-
# do not call shutdown -- force timeout
|
180
|
-
subject.wait_for_termination(0.1).should be_false
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
@@ -1,329 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require_relative 'dereferenceable_shared'
|
3
|
-
require_relative 'obligation_shared'
|
4
|
-
require_relative 'observable_shared'
|
5
|
-
|
6
|
-
module Concurrent
|
7
|
-
|
8
|
-
describe Future do
|
9
|
-
|
10
|
-
let!(:value) { 10 }
|
11
|
-
let(:executor) { PerThreadExecutor.new }
|
12
|
-
|
13
|
-
subject do
|
14
|
-
Future.new(executor: executor){
|
15
|
-
value
|
16
|
-
}.execute.tap{ sleep(0.1) }
|
17
|
-
end
|
18
|
-
|
19
|
-
context 'behavior' do
|
20
|
-
|
21
|
-
# obligation
|
22
|
-
|
23
|
-
let!(:fulfilled_value) { 10 }
|
24
|
-
let!(:rejected_reason) { StandardError.new('mojo jojo') }
|
25
|
-
|
26
|
-
let(:pending_subject) do
|
27
|
-
Future.new(executor: executor){ sleep(3); fulfilled_value }.execute
|
28
|
-
end
|
29
|
-
|
30
|
-
let(:fulfilled_subject) do
|
31
|
-
Future.new(executor: executor){ fulfilled_value }.execute.tap{ sleep(0.1) }
|
32
|
-
end
|
33
|
-
|
34
|
-
let(:rejected_subject) do
|
35
|
-
Future.new(executor: executor){ raise rejected_reason }.execute.tap{ sleep(0.1) }
|
36
|
-
end
|
37
|
-
|
38
|
-
it_should_behave_like :obligation
|
39
|
-
|
40
|
-
# dereferenceable
|
41
|
-
|
42
|
-
def dereferenceable_subject(value, opts = {})
|
43
|
-
opts = opts.merge(executor: executor)
|
44
|
-
Future.new(opts){ value }.execute.tap{ sleep(0.1) }
|
45
|
-
end
|
46
|
-
|
47
|
-
def dereferenceable_observable(opts = {})
|
48
|
-
opts = opts.merge(executor: executor)
|
49
|
-
Future.new(opts){ 'value' }
|
50
|
-
end
|
51
|
-
|
52
|
-
def execute_dereferenceable(subject)
|
53
|
-
subject.execute
|
54
|
-
sleep(0.1)
|
55
|
-
end
|
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
|
69
|
-
end
|
70
|
-
|
71
|
-
context 'subclassing' do
|
72
|
-
|
73
|
-
subject{ Future.execute(executor: executor){ 42 } }
|
74
|
-
|
75
|
-
it 'protects #set' do
|
76
|
-
expect{ subject.set(100) }.to raise_error
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'protects #fail' do
|
80
|
-
expect{ subject.fail }.to raise_error
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'protects #complete' do
|
84
|
-
expect{ subject.complete(true, 100, nil) }.to raise_error
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
context '#initialize' do
|
89
|
-
|
90
|
-
let(:executor) { ImmediateExecutor.new }
|
91
|
-
|
92
|
-
it 'sets the state to :unscheduled' do
|
93
|
-
Future.new(executor: executor){ nil }.should be_unscheduled
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'raises an exception when no block given' do
|
97
|
-
expect {
|
98
|
-
Future.new.execute
|
99
|
-
}.to raise_error(ArgumentError)
|
100
|
-
end
|
101
|
-
|
102
|
-
it 'uses the executor given with the :executor option' do
|
103
|
-
executor.should_receive(:post)
|
104
|
-
Future.execute(executor: executor){ nil }
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'uses the global operation pool when :operation is true' do
|
108
|
-
Concurrent.configuration.should_receive(:global_operation_pool).and_return(executor)
|
109
|
-
Future.execute(operation: true){ nil }
|
110
|
-
end
|
111
|
-
|
112
|
-
it 'uses the global task pool when :task is true' do
|
113
|
-
Concurrent.configuration.should_receive(:global_task_pool).and_return(executor)
|
114
|
-
Future.execute(task: true){ nil }
|
115
|
-
end
|
116
|
-
|
117
|
-
it 'uses the global task pool by default' do
|
118
|
-
Concurrent.configuration.should_receive(:global_task_pool).and_return(executor)
|
119
|
-
Future.execute{ nil }
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
context 'instance #execute' do
|
124
|
-
|
125
|
-
it 'does nothing unless the state is :unscheduled' do
|
126
|
-
executor = ImmediateExecutor.new
|
127
|
-
executor.should_not_receive(:post).with(any_args)
|
128
|
-
future = Future.new(executor: executor){ nil }
|
129
|
-
future.instance_variable_set(:@state, :pending)
|
130
|
-
future.execute
|
131
|
-
future.instance_variable_set(:@state, :rejected)
|
132
|
-
future.execute
|
133
|
-
future.instance_variable_set(:@state, :fulfilled)
|
134
|
-
future.execute
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'posts the block given on construction' do
|
138
|
-
executor.should_receive(:post).with(any_args)
|
139
|
-
future = Future.new(executor: executor){ nil }
|
140
|
-
future.execute
|
141
|
-
end
|
142
|
-
|
143
|
-
it 'sets the state to :pending' do
|
144
|
-
future = Future.new(executor: executor){ sleep(0.1) }
|
145
|
-
future.execute
|
146
|
-
future.should be_pending
|
147
|
-
end
|
148
|
-
|
149
|
-
it 'returns self' do
|
150
|
-
future = Future.new(executor: executor){ nil }
|
151
|
-
future.execute.should be future
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
context 'class #execute' do
|
156
|
-
|
157
|
-
let(:executor) { ImmediateExecutor.new }
|
158
|
-
|
159
|
-
it 'creates a new Future' do
|
160
|
-
future = Future.execute(executor: executor){ nil }
|
161
|
-
future.should be_a(Future)
|
162
|
-
end
|
163
|
-
|
164
|
-
it 'passes the block to the new Future' do
|
165
|
-
@expected = false
|
166
|
-
Future.execute(executor: executor){ @expected = true }
|
167
|
-
@expected.should be_true
|
168
|
-
end
|
169
|
-
|
170
|
-
it 'calls #execute on the new Future' do
|
171
|
-
future = double('future')
|
172
|
-
Future.stub(:new).with(any_args).and_return(future)
|
173
|
-
future.should_receive(:execute).with(no_args)
|
174
|
-
Future.execute{ nil }
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
context 'fulfillment' do
|
179
|
-
|
180
|
-
let(:executor) { ImmediateExecutor.new }
|
181
|
-
|
182
|
-
it 'passes all arguments to handler' do
|
183
|
-
@expected = false
|
184
|
-
Future.new(executor: executor){ @expected = true }.execute
|
185
|
-
@expected.should be_true
|
186
|
-
end
|
187
|
-
|
188
|
-
it 'sets the value to the result of the handler' do
|
189
|
-
future = Future.new(executor: executor){ 42 }.execute
|
190
|
-
future.value.should eq 42
|
191
|
-
end
|
192
|
-
|
193
|
-
it 'sets the state to :fulfilled when the block completes' do
|
194
|
-
future = Future.new(executor: executor){ 42 }.execute
|
195
|
-
future.should be_fulfilled
|
196
|
-
end
|
197
|
-
|
198
|
-
it 'sets the value to nil when the handler raises an exception' do
|
199
|
-
future = Future.new(executor: executor){ raise StandardError }.execute
|
200
|
-
future.value.should be_nil
|
201
|
-
end
|
202
|
-
|
203
|
-
it 'sets the state to :rejected when the handler raises an exception' do
|
204
|
-
future = Future.new(executor: executor){ raise StandardError }.execute
|
205
|
-
future.should be_rejected
|
206
|
-
end
|
207
|
-
|
208
|
-
context 'aliases' do
|
209
|
-
|
210
|
-
it 'aliases #realized? for #fulfilled?' do
|
211
|
-
subject.should be_realized
|
212
|
-
end
|
213
|
-
|
214
|
-
it 'aliases #deref for #value' do
|
215
|
-
subject.deref.should eq value
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
context 'observation' do
|
221
|
-
|
222
|
-
let(:executor) { ImmediateExecutor.new }
|
223
|
-
|
224
|
-
let(:clazz) do
|
225
|
-
Class.new do
|
226
|
-
attr_reader :value
|
227
|
-
attr_reader :reason
|
228
|
-
attr_reader :count
|
229
|
-
define_method(:update) do |time, value, reason|
|
230
|
-
@count = @count.to_i + 1
|
231
|
-
@value = value
|
232
|
-
@reason = reason
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
let(:observer) { clazz.new }
|
238
|
-
|
239
|
-
it 'notifies all observers on fulfillment' do
|
240
|
-
future = Future.new(executor: executor){ 42 }
|
241
|
-
future.add_observer(observer)
|
242
|
-
|
243
|
-
future.execute
|
244
|
-
|
245
|
-
observer.value.should == 42
|
246
|
-
observer.reason.should be_nil
|
247
|
-
end
|
248
|
-
|
249
|
-
it 'notifies all observers on rejection' do
|
250
|
-
future = Future.new(executor: executor){ raise StandardError }
|
251
|
-
future.add_observer(observer)
|
252
|
-
|
253
|
-
future.execute
|
254
|
-
|
255
|
-
observer.value.should be_nil
|
256
|
-
observer.reason.should be_a(StandardError)
|
257
|
-
end
|
258
|
-
|
259
|
-
it 'notifies an observer added after fulfillment' do
|
260
|
-
future = Future.new(executor: executor){ 42 }.execute
|
261
|
-
future.add_observer(observer)
|
262
|
-
observer.value.should == 42
|
263
|
-
end
|
264
|
-
|
265
|
-
it 'notifies an observer added after rejection' do
|
266
|
-
future = Future.new(executor: executor){ raise StandardError }.execute
|
267
|
-
future.add_observer(observer)
|
268
|
-
observer.reason.should be_a(StandardError)
|
269
|
-
end
|
270
|
-
|
271
|
-
it 'does not notify existing observers when a new observer added after fulfillment' do
|
272
|
-
future = Future.new(executor: executor){ 42 }.execute
|
273
|
-
future.add_observer(observer)
|
274
|
-
|
275
|
-
observer.count.should == 1
|
276
|
-
|
277
|
-
o2 = clazz.new
|
278
|
-
future.add_observer(o2)
|
279
|
-
|
280
|
-
observer.count.should == 1
|
281
|
-
o2.value.should == 42
|
282
|
-
end
|
283
|
-
|
284
|
-
it 'does not notify existing observers when a new observer added after rejection' do
|
285
|
-
future = Future.new(executor: executor){ raise StandardError }.execute
|
286
|
-
future.add_observer(observer)
|
287
|
-
|
288
|
-
observer.count.should == 1
|
289
|
-
|
290
|
-
o2 = clazz.new
|
291
|
-
future.add_observer(o2)
|
292
|
-
|
293
|
-
observer.count.should == 1
|
294
|
-
o2.reason.should be_a(StandardError)
|
295
|
-
end
|
296
|
-
|
297
|
-
context 'deadlock avoidance' do
|
298
|
-
|
299
|
-
def reentrant_observer(future)
|
300
|
-
obs = Object.new
|
301
|
-
obs.define_singleton_method(:update) do |time, value, reason|
|
302
|
-
@value = future.value
|
303
|
-
end
|
304
|
-
obs.define_singleton_method(:value) { @value }
|
305
|
-
obs
|
306
|
-
end
|
307
|
-
|
308
|
-
it 'should notify observers outside mutex lock' do
|
309
|
-
future = Future.new(executor: executor){ 42 }
|
310
|
-
obs = reentrant_observer(future)
|
311
|
-
|
312
|
-
future.add_observer(obs)
|
313
|
-
future.execute
|
314
|
-
|
315
|
-
obs.value.should eq 42
|
316
|
-
end
|
317
|
-
|
318
|
-
it 'should notify a new observer added after fulfillment outside lock' do
|
319
|
-
future = Future.new(executor: executor){ 42 }.execute
|
320
|
-
obs = reentrant_observer(future)
|
321
|
-
|
322
|
-
future.add_observer(obs)
|
323
|
-
|
324
|
-
obs.value.should eq 42
|
325
|
-
end
|
326
|
-
end
|
327
|
-
end
|
328
|
-
end
|
329
|
-
end
|