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,77 @@
|
|
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
|
+
context '#value' do
|
57
|
+
|
58
|
+
let(:task){ proc{ nil } }
|
59
|
+
|
60
|
+
it 'does not call the block before #value is called' do
|
61
|
+
task.should_not_receive(:call).with(any_args)
|
62
|
+
Delay.new(&task)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'calls the block when #value is called' do
|
66
|
+
task.should_receive(:call).once.with(any_args).and_return(nil)
|
67
|
+
Delay.new(&task).value
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'only calls the block once no matter how often #value is called' do
|
71
|
+
task.should_receive(:call).once.with(any_args).and_return(nil)
|
72
|
+
delay = Delay.new(&task)
|
73
|
+
5.times{ delay.value }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,66 @@
|
|
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
|
+
value = 0
|
53
|
+
|
54
|
+
t = Thread.new { value = exchanger.exchange(2, 0.2) }
|
55
|
+
|
56
|
+
sleep(0.1)
|
57
|
+
t.status.should eq 'sleep'
|
58
|
+
|
59
|
+
sleep(0.2)
|
60
|
+
|
61
|
+
value.should be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'thread_pool_shared'
|
3
|
+
|
4
|
+
share_examples_for :fixed_thread_pool do
|
5
|
+
|
6
|
+
let!(:num_threads){ 5 }
|
7
|
+
subject { described_class.new(num_threads) }
|
8
|
+
|
9
|
+
after(:each) do
|
10
|
+
subject.kill
|
11
|
+
sleep(0.1)
|
12
|
+
end
|
13
|
+
|
14
|
+
it_should_behave_like :thread_pool
|
15
|
+
|
16
|
+
context '#initialize' do
|
17
|
+
|
18
|
+
it 'raises an exception when the pool length is less than one' do
|
19
|
+
lambda {
|
20
|
+
described_class.new(0)
|
21
|
+
}.should raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context '#min_length' do
|
26
|
+
|
27
|
+
it 'returns :num_threads on creation' do
|
28
|
+
subject.min_length.should eq num_threads
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns :num_threads while running' do
|
32
|
+
10.times{ subject.post{ nil } }
|
33
|
+
sleep(0.1)
|
34
|
+
subject.min_length.should eq num_threads
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'returns :num_threads once shutdown' do
|
38
|
+
10.times{ subject.post{ nil } }
|
39
|
+
sleep(0.1)
|
40
|
+
subject.shutdown
|
41
|
+
subject.wait_for_termination(1)
|
42
|
+
subject.min_length.should eq num_threads
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context '#max_length' do
|
47
|
+
|
48
|
+
it 'returns :num_threads on creation' do
|
49
|
+
subject.max_length.should eq num_threads
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns :num_threads while running' do
|
53
|
+
10.times{ subject.post{ nil } }
|
54
|
+
sleep(0.1)
|
55
|
+
subject.max_length.should eq num_threads
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'returns :num_threads once shutdown' do
|
59
|
+
10.times{ subject.post{ nil } }
|
60
|
+
sleep(0.1)
|
61
|
+
subject.shutdown
|
62
|
+
subject.wait_for_termination(1)
|
63
|
+
subject.max_length.should eq num_threads
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context '#length' do
|
68
|
+
|
69
|
+
it 'returns :num_threads while running' do
|
70
|
+
10.times{ subject.post{ nil } }
|
71
|
+
sleep(0.1)
|
72
|
+
subject.length.should eq num_threads
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context '#largest_length' do
|
77
|
+
|
78
|
+
it 'returns zero on creation' do
|
79
|
+
subject.largest_length.should eq 0
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns :num_threads while running' do
|
83
|
+
10.times{ subject.post{ nil } }
|
84
|
+
sleep(0.1)
|
85
|
+
subject.largest_length.should eq num_threads
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'returns :num_threads once shutdown' do
|
89
|
+
10.times{ subject.post{ nil } }
|
90
|
+
sleep(0.1)
|
91
|
+
subject.shutdown
|
92
|
+
subject.wait_for_termination(1)
|
93
|
+
subject.largest_length.should eq num_threads
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context '#status' do
|
98
|
+
|
99
|
+
it 'returns an array' do
|
100
|
+
subject.stub(:warn)
|
101
|
+
subject.status.should be_kind_of(Array)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context '#idletime' do
|
106
|
+
|
107
|
+
it 'returns zero' do
|
108
|
+
subject.idletime.should eq 0
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context '#kill' do
|
113
|
+
|
114
|
+
it 'attempts to kill all in-progress tasks' do
|
115
|
+
thread_count = [subject.length, 5].max
|
116
|
+
@expected = false
|
117
|
+
thread_count.times{ subject.post{ sleep(1) } }
|
118
|
+
subject.post{ @expected = true }
|
119
|
+
sleep(0.1)
|
120
|
+
subject.kill
|
121
|
+
sleep(0.1)
|
122
|
+
@expected.should be_false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'worker creation and caching' do
|
127
|
+
|
128
|
+
it 'never creates more than :num_threads threads' do
|
129
|
+
pool = described_class.new(5)
|
130
|
+
100.times{ pool << proc{ sleep(1) } }
|
131
|
+
sleep(0.1)
|
132
|
+
pool.current_length.should eq 5
|
133
|
+
pool.kill
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -1,41 +1,37 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require_relative 'dereferenceable_shared'
|
3
3
|
require_relative 'obligation_shared'
|
4
|
-
require_relative 'uses_global_thread_pool_shared'
|
5
4
|
|
6
5
|
module Concurrent
|
7
6
|
|
8
7
|
describe Future do
|
9
8
|
|
10
9
|
let!(:value) { 10 }
|
11
|
-
|
10
|
+
let(:executor) { PerThreadExecutor.new }
|
12
11
|
|
13
|
-
|
14
|
-
Future.
|
12
|
+
subject do
|
13
|
+
Future.new(executor: executor){
|
14
|
+
value
|
15
|
+
}.execute.tap{ sleep(0.1) }
|
15
16
|
end
|
16
17
|
|
17
18
|
context 'behavior' do
|
18
19
|
|
19
|
-
# uses_global_thread_pool
|
20
|
-
|
21
|
-
let!(:thread_pool_user){ Future }
|
22
|
-
it_should_behave_like Concurrent::UsesGlobalThreadPool
|
23
|
-
|
24
20
|
# obligation
|
25
21
|
|
26
22
|
let!(:fulfilled_value) { 10 }
|
27
23
|
let!(:rejected_reason) { StandardError.new('mojo jojo') }
|
28
24
|
|
29
25
|
let(:pending_subject) do
|
30
|
-
Future.new{ sleep(3); fulfilled_value }.execute
|
26
|
+
Future.new(executor: executor){ sleep(3); fulfilled_value }.execute
|
31
27
|
end
|
32
28
|
|
33
29
|
let(:fulfilled_subject) do
|
34
|
-
Future.new{ fulfilled_value }.execute.tap{ sleep(0.1) }
|
30
|
+
Future.new(executor: executor){ fulfilled_value }.execute.tap{ sleep(0.1) }
|
35
31
|
end
|
36
32
|
|
37
33
|
let(:rejected_subject) do
|
38
|
-
Future.new{ raise rejected_reason }.execute.tap{ sleep(0.1) }
|
34
|
+
Future.new(executor: executor){ raise rejected_reason }.execute.tap{ sleep(0.1) }
|
39
35
|
end
|
40
36
|
|
41
37
|
it_should_behave_like :obligation
|
@@ -43,10 +39,12 @@ module Concurrent
|
|
43
39
|
# dereferenceable
|
44
40
|
|
45
41
|
def dereferenceable_subject(value, opts = {})
|
42
|
+
opts = opts.merge(executor: executor)
|
46
43
|
Future.new(opts){ value }.execute.tap{ sleep(0.1) }
|
47
44
|
end
|
48
45
|
|
49
46
|
def dereferenceable_observable(opts = {})
|
47
|
+
opts = opts.merge(executor: executor)
|
50
48
|
Future.new(opts){ 'value' }
|
51
49
|
end
|
52
50
|
|
@@ -59,8 +57,8 @@ module Concurrent
|
|
59
57
|
end
|
60
58
|
|
61
59
|
context 'subclassing' do
|
62
|
-
|
63
|
-
subject{ Future.execute{ 42 } }
|
60
|
+
|
61
|
+
subject{ Future.execute(executor: executor){ 42 } }
|
64
62
|
|
65
63
|
it 'protects #set' do
|
66
64
|
expect{ subject.set(100) }.to raise_error
|
@@ -77,14 +75,10 @@ module Concurrent
|
|
77
75
|
|
78
76
|
context '#initialize' do
|
79
77
|
|
80
|
-
|
81
|
-
Future.new{ nil }.should be_unscheduled
|
82
|
-
end
|
78
|
+
let(:executor) { ImmediateExecutor.new }
|
83
79
|
|
84
|
-
it '
|
85
|
-
Future.
|
86
|
-
Thread.should_not_receive(:new).with(any_args)
|
87
|
-
Future.new{ nil }
|
80
|
+
it 'sets the state to :unscheduled' do
|
81
|
+
Future.new(executor: executor){ nil }.should be_unscheduled
|
88
82
|
end
|
89
83
|
|
90
84
|
it 'raises an exception when no block given' do
|
@@ -92,13 +86,34 @@ module Concurrent
|
|
92
86
|
Future.new.execute
|
93
87
|
}.to raise_error(ArgumentError)
|
94
88
|
end
|
89
|
+
|
90
|
+
it 'uses the executor given with the :executor option' do
|
91
|
+
executor.should_receive(:post)
|
92
|
+
Future.execute(executor: executor){ nil }
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'uses the global operation pool when :operation is true' do
|
96
|
+
Concurrent.configuration.should_receive(:global_operation_pool).and_return(executor)
|
97
|
+
Future.execute(operation: true){ nil }
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'uses the global task pool when :task is true' do
|
101
|
+
Concurrent.configuration.should_receive(:global_task_pool).and_return(executor)
|
102
|
+
Future.execute(task: true){ nil }
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'uses the global task pool by default' do
|
106
|
+
Concurrent.configuration.should_receive(:global_task_pool).and_return(executor)
|
107
|
+
Future.execute{ nil }
|
108
|
+
end
|
95
109
|
end
|
96
110
|
|
97
111
|
context 'instance #execute' do
|
98
112
|
|
99
113
|
it 'does nothing unless the state is :unscheduled' do
|
100
|
-
|
101
|
-
|
114
|
+
executor = ImmediateExecutor.new
|
115
|
+
executor.should_not_receive(:post).with(any_args)
|
116
|
+
future = Future.new(executor: executor){ nil }
|
102
117
|
future.instance_variable_set(:@state, :pending)
|
103
118
|
future.execute
|
104
119
|
future.instance_variable_set(:@state, :rejected)
|
@@ -108,37 +123,35 @@ module Concurrent
|
|
108
123
|
end
|
109
124
|
|
110
125
|
it 'posts the block given on construction' do
|
111
|
-
|
112
|
-
future = Future.new { nil }
|
126
|
+
executor.should_receive(:post).with(any_args)
|
127
|
+
future = Future.new(executor: executor){ nil }
|
113
128
|
future.execute
|
114
129
|
end
|
115
130
|
|
116
131
|
it 'sets the state to :pending' do
|
117
|
-
future = Future.new { sleep(0.1) }
|
132
|
+
future = Future.new(executor: executor){ sleep(0.1) }
|
118
133
|
future.execute
|
119
134
|
future.should be_pending
|
120
135
|
end
|
121
136
|
|
122
137
|
it 'returns self' do
|
123
|
-
future = Future.new { nil }
|
138
|
+
future = Future.new(executor: executor){ nil }
|
124
139
|
future.execute.should be future
|
125
140
|
end
|
126
141
|
end
|
127
142
|
|
128
143
|
context 'class #execute' do
|
129
144
|
|
130
|
-
|
131
|
-
Future.thread_pool = ImmediateExecutor.new
|
132
|
-
end
|
145
|
+
let(:executor) { ImmediateExecutor.new }
|
133
146
|
|
134
147
|
it 'creates a new Future' do
|
135
|
-
future = Future.execute{ nil }
|
148
|
+
future = Future.execute(executor: executor){ nil }
|
136
149
|
future.should be_a(Future)
|
137
150
|
end
|
138
151
|
|
139
152
|
it 'passes the block to the new Future' do
|
140
153
|
@expected = false
|
141
|
-
Future.execute { @expected = true }
|
154
|
+
Future.execute(executor: executor){ @expected = true }
|
142
155
|
@expected.should be_true
|
143
156
|
end
|
144
157
|
|
@@ -152,33 +165,31 @@ module Concurrent
|
|
152
165
|
|
153
166
|
context 'fulfillment' do
|
154
167
|
|
155
|
-
|
156
|
-
Future.thread_pool = ImmediateExecutor.new
|
157
|
-
end
|
168
|
+
let(:executor) { ImmediateExecutor.new }
|
158
169
|
|
159
170
|
it 'passes all arguments to handler' do
|
160
171
|
@expected = false
|
161
|
-
Future.new{ @expected = true }.execute
|
172
|
+
Future.new(executor: executor){ @expected = true }.execute
|
162
173
|
@expected.should be_true
|
163
174
|
end
|
164
175
|
|
165
176
|
it 'sets the value to the result of the handler' do
|
166
|
-
future = Future.new{ 42 }.execute
|
177
|
+
future = Future.new(executor: executor){ 42 }.execute
|
167
178
|
future.value.should eq 42
|
168
179
|
end
|
169
180
|
|
170
181
|
it 'sets the state to :fulfilled when the block completes' do
|
171
|
-
future = Future.new{ 42 }.execute
|
182
|
+
future = Future.new(executor: executor){ 42 }.execute
|
172
183
|
future.should be_fulfilled
|
173
184
|
end
|
174
185
|
|
175
186
|
it 'sets the value to nil when the handler raises an exception' do
|
176
|
-
future = Future.new{ raise StandardError }.execute
|
187
|
+
future = Future.new(executor: executor){ raise StandardError }.execute
|
177
188
|
future.value.should be_nil
|
178
189
|
end
|
179
190
|
|
180
191
|
it 'sets the state to :rejected when the handler raises an exception' do
|
181
|
-
future = Future.new{ raise StandardError }.execute
|
192
|
+
future = Future.new(executor: executor){ raise StandardError }.execute
|
182
193
|
future.should be_rejected
|
183
194
|
end
|
184
195
|
|
@@ -196,9 +207,7 @@ module Concurrent
|
|
196
207
|
|
197
208
|
context 'observation' do
|
198
209
|
|
199
|
-
|
200
|
-
Future.thread_pool = ImmediateExecutor.new
|
201
|
-
end
|
210
|
+
let(:executor) { ImmediateExecutor.new }
|
202
211
|
|
203
212
|
let(:clazz) do
|
204
213
|
Class.new do
|
@@ -216,7 +225,7 @@ module Concurrent
|
|
216
225
|
let(:observer) { clazz.new }
|
217
226
|
|
218
227
|
it 'notifies all observers on fulfillment' do
|
219
|
-
future = Future.new{ 42 }
|
228
|
+
future = Future.new(executor: executor){ 42 }
|
220
229
|
future.add_observer(observer)
|
221
230
|
|
222
231
|
future.execute
|
@@ -226,7 +235,7 @@ module Concurrent
|
|
226
235
|
end
|
227
236
|
|
228
237
|
it 'notifies all observers on rejection' do
|
229
|
-
future = Future.new{ raise StandardError }
|
238
|
+
future = Future.new(executor: executor){ raise StandardError }
|
230
239
|
future.add_observer(observer)
|
231
240
|
|
232
241
|
future.execute
|
@@ -236,19 +245,19 @@ module Concurrent
|
|
236
245
|
end
|
237
246
|
|
238
247
|
it 'notifies an observer added after fulfillment' do
|
239
|
-
future = Future.new{ 42 }.execute
|
248
|
+
future = Future.new(executor: executor){ 42 }.execute
|
240
249
|
future.add_observer(observer)
|
241
250
|
observer.value.should == 42
|
242
251
|
end
|
243
252
|
|
244
253
|
it 'notifies an observer added after rejection' do
|
245
|
-
future = Future.new{ raise StandardError }.execute
|
254
|
+
future = Future.new(executor: executor){ raise StandardError }.execute
|
246
255
|
future.add_observer(observer)
|
247
256
|
observer.reason.should be_a(StandardError)
|
248
257
|
end
|
249
258
|
|
250
259
|
it 'does not notify existing observers when a new observer added after fulfillment' do
|
251
|
-
future = Future.new{ 42 }.execute
|
260
|
+
future = Future.new(executor: executor){ 42 }.execute
|
252
261
|
future.add_observer(observer)
|
253
262
|
|
254
263
|
observer.count.should == 1
|
@@ -261,7 +270,7 @@ module Concurrent
|
|
261
270
|
end
|
262
271
|
|
263
272
|
it 'does not notify existing observers when a new observer added after rejection' do
|
264
|
-
future = Future.new{ raise StandardError }.execute
|
273
|
+
future = Future.new(executor: executor){ raise StandardError }.execute
|
265
274
|
future.add_observer(observer)
|
266
275
|
|
267
276
|
observer.count.should == 1
|
@@ -285,7 +294,7 @@ module Concurrent
|
|
285
294
|
end
|
286
295
|
|
287
296
|
it 'should notify observers outside mutex lock' do
|
288
|
-
future = Future.new{ 42 }
|
297
|
+
future = Future.new(executor: executor){ 42 }
|
289
298
|
obs = reentrant_observer(future)
|
290
299
|
|
291
300
|
future.add_observer(obs)
|
@@ -295,7 +304,7 @@ module Concurrent
|
|
295
304
|
end
|
296
305
|
|
297
306
|
it 'should notify a new observer added after fulfillment outside lock' do
|
298
|
-
future = Future.new{ 42 }.execute
|
307
|
+
future = Future.new(executor: executor){ 42 }.execute
|
299
308
|
obs = reentrant_observer(future)
|
300
309
|
|
301
310
|
future.add_observer(obs)
|