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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -77
  3. data/lib/concurrent.rb +17 -2
  4. data/lib/concurrent/actor.rb +17 -0
  5. data/lib/concurrent/actor_context.rb +31 -0
  6. data/lib/concurrent/actor_ref.rb +39 -0
  7. data/lib/concurrent/agent.rb +12 -3
  8. data/lib/concurrent/async.rb +290 -0
  9. data/lib/concurrent/atomic.rb +5 -9
  10. data/lib/concurrent/cached_thread_pool.rb +39 -137
  11. data/lib/concurrent/channel/blocking_ring_buffer.rb +60 -0
  12. data/lib/concurrent/channel/buffered_channel.rb +83 -0
  13. data/lib/concurrent/channel/channel.rb +11 -0
  14. data/lib/concurrent/channel/probe.rb +19 -0
  15. data/lib/concurrent/channel/ring_buffer.rb +54 -0
  16. data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
  17. data/lib/concurrent/channel/waitable_list.rb +38 -0
  18. data/lib/concurrent/configuration.rb +92 -0
  19. data/lib/concurrent/dataflow.rb +9 -3
  20. data/lib/concurrent/delay.rb +88 -0
  21. data/lib/concurrent/exchanger.rb +31 -0
  22. data/lib/concurrent/fixed_thread_pool.rb +28 -122
  23. data/lib/concurrent/future.rb +10 -5
  24. data/lib/concurrent/immediate_executor.rb +3 -2
  25. data/lib/concurrent/ivar.rb +2 -1
  26. data/lib/concurrent/java_cached_thread_pool.rb +45 -0
  27. data/lib/concurrent/java_fixed_thread_pool.rb +37 -0
  28. data/lib/concurrent/java_thread_pool_executor.rb +194 -0
  29. data/lib/concurrent/per_thread_executor.rb +23 -0
  30. data/lib/concurrent/postable.rb +2 -0
  31. data/lib/concurrent/processor_count.rb +125 -0
  32. data/lib/concurrent/promise.rb +42 -18
  33. data/lib/concurrent/ruby_cached_thread_pool.rb +37 -0
  34. data/lib/concurrent/ruby_fixed_thread_pool.rb +31 -0
  35. data/lib/concurrent/ruby_thread_pool_executor.rb +268 -0
  36. data/lib/concurrent/ruby_thread_pool_worker.rb +69 -0
  37. data/lib/concurrent/simple_actor_ref.rb +124 -0
  38. data/lib/concurrent/thread_local_var.rb +1 -1
  39. data/lib/concurrent/thread_pool_executor.rb +30 -0
  40. data/lib/concurrent/timer_task.rb +13 -10
  41. data/lib/concurrent/tvar.rb +212 -0
  42. data/lib/concurrent/utilities.rb +1 -0
  43. data/lib/concurrent/version.rb +1 -1
  44. data/spec/concurrent/actor_context_spec.rb +37 -0
  45. data/spec/concurrent/actor_ref_shared.rb +313 -0
  46. data/spec/concurrent/actor_spec.rb +9 -1
  47. data/spec/concurrent/agent_spec.rb +97 -96
  48. data/spec/concurrent/async_spec.rb +320 -0
  49. data/spec/concurrent/cached_thread_pool_shared.rb +137 -0
  50. data/spec/concurrent/channel/blocking_ring_buffer_spec.rb +149 -0
  51. data/spec/concurrent/channel/buffered_channel_spec.rb +151 -0
  52. data/spec/concurrent/channel/channel_spec.rb +37 -0
  53. data/spec/concurrent/channel/probe_spec.rb +49 -0
  54. data/spec/concurrent/channel/ring_buffer_spec.rb +126 -0
  55. data/spec/concurrent/channel/unbuffered_channel_spec.rb +132 -0
  56. data/spec/concurrent/configuration_spec.rb +134 -0
  57. data/spec/concurrent/dataflow_spec.rb +109 -27
  58. data/spec/concurrent/delay_spec.rb +77 -0
  59. data/spec/concurrent/exchanger_spec.rb +66 -0
  60. data/spec/concurrent/fixed_thread_pool_shared.rb +136 -0
  61. data/spec/concurrent/future_spec.rb +60 -51
  62. data/spec/concurrent/global_thread_pool_shared.rb +33 -0
  63. data/spec/concurrent/immediate_executor_spec.rb +4 -25
  64. data/spec/concurrent/ivar_spec.rb +36 -23
  65. data/spec/concurrent/java_cached_thread_pool_spec.rb +64 -0
  66. data/spec/concurrent/java_fixed_thread_pool_spec.rb +64 -0
  67. data/spec/concurrent/java_thread_pool_executor_spec.rb +71 -0
  68. data/spec/concurrent/obligation_shared.rb +32 -20
  69. data/spec/concurrent/{global_thread_pool_spec.rb → per_thread_executor_spec.rb} +9 -13
  70. data/spec/concurrent/processor_count_spec.rb +20 -0
  71. data/spec/concurrent/promise_spec.rb +29 -41
  72. data/spec/concurrent/ruby_cached_thread_pool_spec.rb +69 -0
  73. data/spec/concurrent/ruby_fixed_thread_pool_spec.rb +39 -0
  74. data/spec/concurrent/ruby_thread_pool_executor_spec.rb +183 -0
  75. data/spec/concurrent/simple_actor_ref_spec.rb +219 -0
  76. data/spec/concurrent/thread_pool_class_cast_spec.rb +40 -0
  77. data/spec/concurrent/thread_pool_executor_shared.rb +155 -0
  78. data/spec/concurrent/thread_pool_shared.rb +98 -36
  79. data/spec/concurrent/tvar_spec.rb +137 -0
  80. data/spec/spec_helper.rb +4 -0
  81. data/spec/support/functions.rb +4 -0
  82. metadata +85 -20
  83. data/lib/concurrent/cached_thread_pool/worker.rb +0 -91
  84. data/lib/concurrent/channel.rb +0 -63
  85. data/lib/concurrent/fixed_thread_pool/worker.rb +0 -54
  86. data/lib/concurrent/global_thread_pool.rb +0 -42
  87. data/spec/concurrent/cached_thread_pool_spec.rb +0 -101
  88. data/spec/concurrent/channel_spec.rb +0 -86
  89. data/spec/concurrent/fixed_thread_pool_spec.rb +0 -92
  90. 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
- subject { Future.new{ value }.execute.tap{ sleep(0.1) } }
10
+ let(:executor) { PerThreadExecutor.new }
12
11
 
13
- before(:each) do
14
- Future.thread_pool = FixedThreadPool.new(1)
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
- it 'sets the state to :unscheduled' do
81
- Future.new{ nil }.should be_unscheduled
82
- end
78
+ let(:executor) { ImmediateExecutor.new }
83
79
 
84
- it 'does not spawn a new thread' do
85
- Future.thread_pool.should_not_receive(:post).with(any_args)
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
- Future.should_not_receive(:thread_pool).with(any_args)
101
- future = Future.new{ nil }
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
- Future.thread_pool.should_receive(:post).with(any_args)
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
- before(:each) do
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
- before(:each) do
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
- before(:each) do
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)