concurrent-ruby 0.5.0 → 0.6.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
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)