concurrent-ruby 0.6.0 → 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.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +55 -51
  3. data/lib/concurrent/actress/ad_hoc.rb +6 -0
  4. data/lib/concurrent/actress/context.rb +10 -8
  5. data/lib/concurrent/actress/core.rb +44 -20
  6. data/lib/concurrent/actress/core_delegations.rb +5 -0
  7. data/lib/concurrent/actress/envelope.rb +21 -5
  8. data/lib/concurrent/actress/reference.rb +5 -5
  9. data/lib/concurrent/actress.rb +152 -6
  10. data/lib/concurrent/agent.rb +9 -9
  11. data/lib/concurrent/atomic.rb +46 -0
  12. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
  13. data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
  14. data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
  15. data/lib/concurrent/atomic_reference/jruby.rb +8 -0
  16. data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
  17. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
  18. data/lib/concurrent/atomic_reference/rbx.rb +16 -0
  19. data/lib/concurrent/atomic_reference/ruby.rb +16 -0
  20. data/lib/concurrent/atomics.rb +1 -1
  21. data/lib/concurrent/configuration.rb +7 -2
  22. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +0 -1
  23. data/lib/concurrent/executor/{one_by_one.rb → serialized_execution.rb} +21 -8
  24. data/lib/concurrent/executor/timer_set.rb +6 -1
  25. data/lib/concurrent/executors.rb +1 -1
  26. data/lib/concurrent/logging.rb +1 -1
  27. data/lib/concurrent/observable.rb +1 -1
  28. data/lib/concurrent/supervisor.rb +1 -1
  29. data/lib/concurrent/timer_task.rb +0 -36
  30. data/lib/concurrent/version.rb +1 -1
  31. data/lib/concurrent.rb +3 -4
  32. data/lib/concurrent_ruby_ext.so +0 -0
  33. data/lib/extension_helper.rb +9 -0
  34. metadata +19 -152
  35. data/lib/concurrent/actor/actor.rb +0 -270
  36. data/lib/concurrent/actor/postable.rb +0 -102
  37. data/lib/concurrent/actors.rb +0 -2
  38. data/lib/concurrent/actress/doc.md +0 -53
  39. data/lib/concurrent/atomic/atomic.rb +0 -48
  40. data/lib/concurrent/runnable.rb +0 -90
  41. data/lib/concurrent/stoppable.rb +0 -20
  42. data/spec/concurrent/actor/actor_spec.rb +0 -376
  43. data/spec/concurrent/actor/postable_shared.rb +0 -218
  44. data/spec/concurrent/actress_spec.rb +0 -191
  45. data/spec/concurrent/agent_spec.rb +0 -500
  46. data/spec/concurrent/async_spec.rb +0 -352
  47. data/spec/concurrent/atomic/atomic_boolean_spec.rb +0 -172
  48. data/spec/concurrent/atomic/atomic_fixnum_spec.rb +0 -186
  49. data/spec/concurrent/atomic/atomic_spec.rb +0 -133
  50. data/spec/concurrent/atomic/condition_spec.rb +0 -171
  51. data/spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb +0 -10
  52. data/spec/concurrent/atomic/copy_on_write_observer_set_spec.rb +0 -10
  53. data/spec/concurrent/atomic/count_down_latch_spec.rb +0 -151
  54. data/spec/concurrent/atomic/cyclic_barrier_spec.rb +0 -248
  55. data/spec/concurrent/atomic/event_spec.rb +0 -200
  56. data/spec/concurrent/atomic/observer_set_shared.rb +0 -242
  57. data/spec/concurrent/atomic/thread_local_var_spec.rb +0 -113
  58. data/spec/concurrent/channel/buffered_channel_spec.rb +0 -151
  59. data/spec/concurrent/channel/channel_spec.rb +0 -39
  60. data/spec/concurrent/channel/probe_spec.rb +0 -77
  61. data/spec/concurrent/channel/unbuffered_channel_spec.rb +0 -132
  62. data/spec/concurrent/collection/blocking_ring_buffer_spec.rb +0 -149
  63. data/spec/concurrent/collection/priority_queue_spec.rb +0 -317
  64. data/spec/concurrent/collection/ring_buffer_spec.rb +0 -126
  65. data/spec/concurrent/configuration_spec.rb +0 -71
  66. data/spec/concurrent/dataflow_spec.rb +0 -242
  67. data/spec/concurrent/delay_spec.rb +0 -91
  68. data/spec/concurrent/dereferenceable_shared.rb +0 -146
  69. data/spec/concurrent/exchanger_spec.rb +0 -66
  70. data/spec/concurrent/executor/cached_thread_pool_shared.rb +0 -115
  71. data/spec/concurrent/executor/fixed_thread_pool_shared.rb +0 -136
  72. data/spec/concurrent/executor/global_thread_pool_shared.rb +0 -35
  73. data/spec/concurrent/executor/immediate_executor_spec.rb +0 -12
  74. data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +0 -44
  75. data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +0 -64
  76. data/spec/concurrent/executor/java_single_thread_executor_spec.rb +0 -21
  77. data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +0 -71
  78. data/spec/concurrent/executor/per_thread_executor_spec.rb +0 -57
  79. data/spec/concurrent/executor/ruby_cached_thread_pool_spec.rb +0 -69
  80. data/spec/concurrent/executor/ruby_fixed_thread_pool_spec.rb +0 -39
  81. data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +0 -18
  82. data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +0 -171
  83. data/spec/concurrent/executor/safe_task_executor_spec.rb +0 -103
  84. data/spec/concurrent/executor/thread_pool_class_cast_spec.rb +0 -52
  85. data/spec/concurrent/executor/thread_pool_executor_shared.rb +0 -155
  86. data/spec/concurrent/executor/thread_pool_shared.rb +0 -239
  87. data/spec/concurrent/executor/timer_set_spec.rb +0 -183
  88. data/spec/concurrent/future_spec.rb +0 -329
  89. data/spec/concurrent/ivar_spec.rb +0 -215
  90. data/spec/concurrent/mvar_spec.rb +0 -380
  91. data/spec/concurrent/obligation_shared.rb +0 -102
  92. data/spec/concurrent/obligation_spec.rb +0 -282
  93. data/spec/concurrent/observable_shared.rb +0 -177
  94. data/spec/concurrent/observable_spec.rb +0 -56
  95. data/spec/concurrent/options_parser_spec.rb +0 -71
  96. data/spec/concurrent/promise_spec.rb +0 -367
  97. data/spec/concurrent/runnable_shared.rb +0 -68
  98. data/spec/concurrent/runnable_spec.rb +0 -235
  99. data/spec/concurrent/scheduled_task_spec.rb +0 -342
  100. data/spec/concurrent/stoppable_shared.rb +0 -37
  101. data/spec/concurrent/supervisor_spec.rb +0 -1148
  102. data/spec/concurrent/timer_task_spec.rb +0 -258
  103. data/spec/concurrent/tvar_spec.rb +0 -137
  104. data/spec/concurrent/utility/processor_count_spec.rb +0 -20
  105. data/spec/concurrent/utility/timeout_spec.rb +0 -50
  106. data/spec/concurrent/utility/timer_spec.rb +0 -52
  107. data/spec/spec_helper.rb +0 -31
  108. data/spec/support/example_group_extensions.rb +0 -48
  109. data/spec/support/less_than_or_equal_to_matcher.rb +0 -5
  110. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -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
@@ -1,215 +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 IVar do
9
-
10
- let!(:value) { 10 }
11
-
12
- subject do
13
- i = IVar.new
14
- i.set(14)
15
- i
16
- end
17
-
18
- context 'behavior' do
19
-
20
- # obligation
21
-
22
- let!(:fulfilled_value) { 10 }
23
- let(:rejected_reason) { StandardError.new('Boom!') }
24
-
25
- let(:pending_subject) do
26
- @i = IVar.new
27
- Thread.new do
28
- sleep(3)
29
- @i.set(fulfilled_value)
30
- end
31
- @i
32
- end
33
-
34
- let(:fulfilled_subject) do
35
- i = IVar.new
36
- i.set(fulfilled_value)
37
- i
38
- end
39
-
40
- let(:rejected_subject) do
41
- i = IVar.new
42
- i.fail(rejected_reason)
43
- i
44
- end
45
-
46
- it_should_behave_like :obligation
47
-
48
- # dereferenceable
49
-
50
- def dereferenceable_subject(value, opts = {})
51
- IVar.new(value, opts)
52
- end
53
-
54
- def dereferenceable_observable(opts = {})
55
- IVar.new(IVar::NO_VALUE, opts)
56
- end
57
-
58
- def execute_dereferenceable(subject)
59
- subject.set('value')
60
- end
61
-
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
73
- end
74
-
75
- context '#initialize' do
76
-
77
- it 'does not have to set an initial value' do
78
- i = IVar.new
79
- i.should be_incomplete
80
- end
81
-
82
- it 'does not set an initial value if you pass NO_VALUE' do
83
- i = IVar.new(IVar::NO_VALUE)
84
- i.should be_incomplete
85
- end
86
-
87
- it 'can set an initial value' do
88
- i = IVar.new(14)
89
- i.should be_completed
90
- end
91
-
92
- end
93
-
94
- context '#set' do
95
-
96
- it 'sets the state to be fulfilled' do
97
- i = IVar.new
98
- i.set(14)
99
- i.should be_fulfilled
100
- end
101
-
102
- it 'sets the value' do
103
- i = IVar.new
104
- i.set(14)
105
- i.value.should eq 14
106
- end
107
-
108
- it 'raises an exception if set more than once' do
109
- i = IVar.new
110
- i.set(14)
111
- expect {i.set(2)}.to raise_error(Concurrent::MultipleAssignmentError)
112
- i.value.should eq 14
113
- end
114
-
115
- it 'returns self' do
116
- i = IVar.new
117
- i.set(42).should eq i
118
- end
119
- end
120
-
121
- context '#fail' do
122
-
123
- it 'sets the state to be rejected' do
124
- i = IVar.new
125
- i.fail
126
- i.should be_rejected
127
- end
128
-
129
- it 'sets the value to be nil' do
130
- i = IVar.new
131
- i.fail
132
- i.value.should be_nil
133
- end
134
-
135
- it 'raises an exception if set more than once' do
136
- i = IVar.new
137
- i.fail
138
- expect {i.fail}.to raise_error(Concurrent::MultipleAssignmentError)
139
- i.value.should be_nil
140
- end
141
-
142
- it 'defaults the reason to a StandardError' do
143
- i = IVar.new
144
- i.fail
145
- i.reason.should be_a StandardError
146
- end
147
-
148
- it 'returns self' do
149
- i = IVar.new
150
- i.fail.should eq i
151
- end
152
- end
153
-
154
- context 'observation' do
155
-
156
- let(:clazz) do
157
- Class.new do
158
- attr_reader :value
159
- attr_reader :reason
160
- attr_reader :count
161
- define_method(:update) do |time, value, reason|
162
- @count = @count.to_i + 1
163
- @value = value
164
- @reason = reason
165
- end
166
- end
167
- end
168
-
169
- let(:observer) { clazz.new }
170
-
171
- it 'notifies all observers on #set' do
172
- i = IVar.new
173
- i.add_observer(observer)
174
-
175
- i.set(42)
176
-
177
- observer.value.should == 42
178
- observer.reason.should be_nil
179
- end
180
-
181
- context 'deadlock avoidance' do
182
-
183
- def reentrant_observer(i)
184
- obs = Object.new
185
- obs.define_singleton_method(:update) do |time, value, reason|
186
- @value = i.value
187
- end
188
- obs.define_singleton_method(:value) { @value }
189
- obs
190
- end
191
-
192
- it 'should notify observers outside mutex lock' do
193
- i = IVar.new
194
- obs = reentrant_observer(i)
195
-
196
- i.add_observer(obs)
197
- i.set(42)
198
-
199
- obs.value.should eq 42
200
- end
201
-
202
- it 'should notify a new observer added after fulfillment outside lock' do
203
- i = IVar.new
204
- i.set(42)
205
- obs = reentrant_observer(i)
206
-
207
- i.add_observer(obs)
208
-
209
- obs.value.should eq 42
210
- end
211
- end
212
-
213
- end
214
- end
215
- end