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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/concurrent.rb +3 -4
  4. data/lib/concurrent/atomic.rb +46 -0
  5. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
  6. data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
  7. data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
  8. data/lib/concurrent/atomic_reference/jruby.rb +8 -0
  9. data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
  10. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
  11. data/lib/concurrent/atomic_reference/rbx.rb +16 -0
  12. data/lib/concurrent/atomic_reference/ruby.rb +16 -0
  13. data/lib/concurrent/atomics.rb +1 -1
  14. data/lib/concurrent/configuration.rb +1 -1
  15. data/lib/concurrent/supervisor.rb +1 -1
  16. data/lib/concurrent/timer_task.rb +0 -36
  17. data/lib/concurrent/version.rb +1 -1
  18. data/lib/concurrent_ruby_ext.so +0 -0
  19. data/lib/extension_helper.rb +9 -0
  20. metadata +16 -148
  21. data/lib/concurrent/actor/actor.rb +0 -270
  22. data/lib/concurrent/actor/postable.rb +0 -102
  23. data/lib/concurrent/actors.rb +0 -2
  24. data/lib/concurrent/atomic/atomic.rb +0 -48
  25. data/lib/concurrent/runnable.rb +0 -90
  26. data/lib/concurrent/stoppable.rb +0 -20
  27. data/spec/concurrent/actor/actor_spec.rb +0 -376
  28. data/spec/concurrent/actor/postable_shared.rb +0 -218
  29. data/spec/concurrent/actress_spec.rb +0 -211
  30. data/spec/concurrent/agent_spec.rb +0 -500
  31. data/spec/concurrent/async_spec.rb +0 -352
  32. data/spec/concurrent/atomic/atomic_boolean_spec.rb +0 -172
  33. data/spec/concurrent/atomic/atomic_fixnum_spec.rb +0 -186
  34. data/spec/concurrent/atomic/atomic_spec.rb +0 -133
  35. data/spec/concurrent/atomic/condition_spec.rb +0 -171
  36. data/spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb +0 -10
  37. data/spec/concurrent/atomic/copy_on_write_observer_set_spec.rb +0 -10
  38. data/spec/concurrent/atomic/count_down_latch_spec.rb +0 -151
  39. data/spec/concurrent/atomic/cyclic_barrier_spec.rb +0 -248
  40. data/spec/concurrent/atomic/event_spec.rb +0 -200
  41. data/spec/concurrent/atomic/observer_set_shared.rb +0 -242
  42. data/spec/concurrent/atomic/thread_local_var_spec.rb +0 -113
  43. data/spec/concurrent/channel/buffered_channel_spec.rb +0 -151
  44. data/spec/concurrent/channel/channel_spec.rb +0 -39
  45. data/spec/concurrent/channel/probe_spec.rb +0 -77
  46. data/spec/concurrent/channel/unbuffered_channel_spec.rb +0 -132
  47. data/spec/concurrent/collection/blocking_ring_buffer_spec.rb +0 -149
  48. data/spec/concurrent/collection/priority_queue_spec.rb +0 -317
  49. data/spec/concurrent/collection/ring_buffer_spec.rb +0 -126
  50. data/spec/concurrent/configuration_spec.rb +0 -69
  51. data/spec/concurrent/dataflow_spec.rb +0 -242
  52. data/spec/concurrent/delay_spec.rb +0 -91
  53. data/spec/concurrent/dereferenceable_shared.rb +0 -146
  54. data/spec/concurrent/exchanger_spec.rb +0 -66
  55. data/spec/concurrent/executor/cached_thread_pool_shared.rb +0 -115
  56. data/spec/concurrent/executor/fixed_thread_pool_shared.rb +0 -136
  57. data/spec/concurrent/executor/global_thread_pool_shared.rb +0 -35
  58. data/spec/concurrent/executor/immediate_executor_spec.rb +0 -12
  59. data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +0 -44
  60. data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +0 -64
  61. data/spec/concurrent/executor/java_single_thread_executor_spec.rb +0 -21
  62. data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +0 -71
  63. data/spec/concurrent/executor/per_thread_executor_spec.rb +0 -57
  64. data/spec/concurrent/executor/ruby_cached_thread_pool_spec.rb +0 -69
  65. data/spec/concurrent/executor/ruby_fixed_thread_pool_spec.rb +0 -39
  66. data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +0 -18
  67. data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +0 -171
  68. data/spec/concurrent/executor/safe_task_executor_spec.rb +0 -103
  69. data/spec/concurrent/executor/thread_pool_class_cast_spec.rb +0 -52
  70. data/spec/concurrent/executor/thread_pool_executor_shared.rb +0 -155
  71. data/spec/concurrent/executor/thread_pool_shared.rb +0 -269
  72. data/spec/concurrent/executor/timer_set_spec.rb +0 -183
  73. data/spec/concurrent/future_spec.rb +0 -329
  74. data/spec/concurrent/ivar_spec.rb +0 -215
  75. data/spec/concurrent/mvar_spec.rb +0 -380
  76. data/spec/concurrent/obligation_shared.rb +0 -102
  77. data/spec/concurrent/obligation_spec.rb +0 -282
  78. data/spec/concurrent/observable_shared.rb +0 -177
  79. data/spec/concurrent/observable_spec.rb +0 -56
  80. data/spec/concurrent/options_parser_spec.rb +0 -71
  81. data/spec/concurrent/promise_spec.rb +0 -367
  82. data/spec/concurrent/runnable_shared.rb +0 -68
  83. data/spec/concurrent/runnable_spec.rb +0 -235
  84. data/spec/concurrent/scheduled_task_spec.rb +0 -340
  85. data/spec/concurrent/stoppable_shared.rb +0 -37
  86. data/spec/concurrent/supervisor_spec.rb +0 -1149
  87. data/spec/concurrent/timer_task_spec.rb +0 -256
  88. data/spec/concurrent/tvar_spec.rb +0 -137
  89. data/spec/concurrent/utility/processor_count_spec.rb +0 -20
  90. data/spec/concurrent/utility/timeout_spec.rb +0 -50
  91. data/spec/concurrent/utility/timer_spec.rb +0 -52
  92. data/spec/spec_helper.rb +0 -41
  93. data/spec/support/example_group_extensions.rb +0 -52
  94. data/spec/support/less_than_or_equal_to_matcher.rb +0 -5
@@ -1,211 +0,0 @@
1
- require 'spec_helper'
2
- require 'concurrent/actress'
3
-
4
- module Concurrent
5
- module Actress
6
- i_know_it_is_experimental!
7
-
8
- class Reference
9
- def backdoor(&block)
10
- core.send :schedule_execution do
11
- core.instance_eval &block
12
- end
13
- end
14
- end
15
-
16
- describe 'Concurrent::Actress' do
17
- prepend_before do
18
- do_no_reset!
19
- end
20
-
21
- def terminate_actors(*actors)
22
- actors.each do |actor|
23
- actor.backdoor { terminate! }
24
- actor.terminated.wait
25
- end
26
- end
27
-
28
- class Ping
29
- include Context
30
-
31
- def initialize(queue)
32
- @queue = queue
33
- end
34
-
35
- def on_message(message)
36
- case message
37
- when :terminate
38
- terminate!
39
- when :child
40
- AdHoc.spawn(:pong, @queue) { |queue| -> m { queue << m } }
41
- else
42
- @queue << message
43
- message
44
- end
45
- end
46
- end
47
-
48
- # def trace!
49
- # set_trace_func proc { |event, file, line, id, binding, classname|
50
- # # thread = eval('Thread.current', binding).object_id.to_s(16)
51
- # printf "%8s %20s %20s %s %s:%-2d\n", event, id, classname, nil, file, line
52
- # }
53
- # yield
54
- # ensure
55
- # set_trace_func nil
56
- # end
57
-
58
- describe 'stress test' do
59
- 1.times do |i|
60
- it format('run %3d', i) do
61
- # puts format('run %3d', i)
62
- Array.new(10).map do
63
- Thread.new do
64
- 10.times do
65
- # trace! do
66
- queue = Queue.new
67
- actor = Ping.spawn :ping, queue
68
-
69
- # when spawn returns children are set
70
- Concurrent::Actress::ROOT.send(:core).instance_variable_get(:@children).should include(actor)
71
-
72
- actor << 'a' << 1
73
- queue.pop.should eq 'a'
74
- actor.ask(2).value.should eq 2
75
-
76
- actor.parent.should eq Concurrent::Actress::ROOT
77
- Concurrent::Actress::ROOT.path.should eq '/'
78
- actor.path.should eq '/ping'
79
- child = actor.ask(:child).value
80
- child.path.should eq '/ping/pong'
81
- queue.clear
82
- child.ask(3)
83
- queue.pop.should eq 3
84
-
85
- actor << :terminate
86
- actor.ask(:blow_up).wait.should be_rejected
87
- terminate_actors actor, child
88
- end
89
- end
90
- end.each(&:join)
91
- end
92
- end
93
- end
94
-
95
- describe 'spawning' do
96
- describe 'Actress#spawn' do
97
- behaviour = -> v { -> _ { v } }
98
- subjects = { spawn: -> { Actress.spawn(AdHoc, :ping, 'arg', &behaviour) },
99
- context_spawn: -> { AdHoc.spawn(:ping, 'arg', &behaviour) },
100
- spawn_by_hash: -> { Actress.spawn(class: AdHoc, name: :ping, args: ['arg'], &behaviour) },
101
- context_spawn_by_hash: -> { AdHoc.spawn(name: :ping, args: ['arg'], &behaviour) } }
102
-
103
- subjects.each do |desc, subject_definition|
104
- describe desc do
105
- subject &subject_definition
106
- after { terminate_actors subject }
107
- its(:path) { should eq '/ping' }
108
- its(:parent) { should eq ROOT }
109
- its(:name) { should eq 'ping' }
110
- it('executor should be global') { subject.executor.should eq Concurrent.configuration.global_task_pool }
111
- its(:reference) { should eq subject }
112
- it 'returns ars' do
113
- subject.ask!(:anything).should eq 'arg'
114
- end
115
- end
116
- end
117
- end
118
-
119
- it 'terminates on failed initialization' do
120
- a = AdHoc.spawn(name: :fail, logger: Concurrent.configuration.no_logger) { raise }
121
- a.ask(nil).wait.rejected?.should be_true
122
- a.terminated?.should be_true
123
- end
124
-
125
- it 'terminates on failed initialization and raises with spawn!' do
126
- expect do
127
- AdHoc.spawn!(name: :fail, logger: Concurrent.configuration.no_logger) { raise 'm' }
128
- end.to raise_error(StandardError, 'm')
129
- end
130
-
131
- it 'terminates on failed message processing' do
132
- a = AdHoc.spawn(name: :fail, logger: Concurrent.configuration.no_logger) { -> _ { raise } }
133
- a.ask(nil).wait.rejected?.should be_true
134
- a.terminated?.should be_true
135
- end
136
- end
137
-
138
- describe 'messaging' do
139
- subject { AdHoc.spawn(:add) { c = 0; -> v { c = c + v } } }
140
- specify do
141
- subject.tell(1).tell(1)
142
- subject << 1 << 1
143
- subject.ask(0).value!.should eq 4
144
- end
145
- after { terminate_actors subject }
146
- end
147
-
148
- describe 'children' do
149
- let(:parent) do
150
- AdHoc.spawn(:parent) do
151
- -> message do
152
- if message == :child
153
- AdHoc.spawn(:child) { -> _ { parent } }
154
- else
155
- children
156
- end
157
- end
158
- end
159
- end
160
-
161
- it 'has children set after a child is created' do
162
- child = parent.ask!(:child)
163
- parent.ask!(nil).should include(child)
164
- child.ask!(nil).should eq parent
165
-
166
- terminate_actors parent, child
167
- end
168
- end
169
-
170
- describe 'envelope' do
171
- subject { AdHoc.spawn(:subject) { -> _ { envelope } } }
172
- specify do
173
- envelope = subject.ask!('a')
174
- envelope.should be_a_kind_of Envelope
175
- envelope.message.should eq 'a'
176
- envelope.ivar.should be_completed
177
- envelope.ivar.value.should eq envelope
178
- envelope.sender.should eq Thread.current
179
- terminate_actors subject
180
- end
181
- end
182
-
183
- describe 'termination' do
184
- subject do
185
- AdHoc.spawn(:parent) do
186
- child = AdHoc.spawn(:child) { -> v { v } }
187
- -> v do
188
- if v == :terminate
189
- terminate!
190
- else
191
- child
192
- end
193
- end
194
- end
195
- end
196
-
197
- it 'terminates with all its children' do
198
- child = subject.ask! :child
199
- subject.terminated?.should be_false
200
- subject.ask(:terminate).wait
201
- subject.terminated?.should be_true
202
- child.terminated.wait
203
- child.terminated?.should be_true
204
-
205
- terminate_actors subject, child
206
- end
207
- end
208
-
209
- end
210
- end
211
- end
@@ -1,500 +0,0 @@
1
- require 'spec_helper'
2
- require_relative 'dereferenceable_shared'
3
- require_relative 'observable_shared'
4
-
5
- module Concurrent
6
-
7
- describe Agent do
8
-
9
- let(:executor) { PerThreadExecutor.new }
10
-
11
- subject { Agent.new(0, executor: executor) }
12
-
13
- let(:observer) do
14
- Class.new do
15
- attr_reader :value
16
- define_method(:update) do |time, value|
17
- @value = value
18
- end
19
- end.new
20
- end
21
-
22
- context '#send_off' do
23
- subject { Agent.new 2, executor: executor }
24
-
25
- it 'executes post and post-off in order' do
26
- subject.post { |v| v + 2 }
27
- subject.post_off { |v| v * 3 }
28
- subject.await
29
- subject.value.should eq 12
30
- end
31
- end
32
-
33
- context 'behavior' do
34
-
35
- # dereferenceable
36
-
37
- def dereferenceable_subject(value, opts = {})
38
- opts = opts.merge(executor: executor)
39
- Agent.new(value, opts)
40
- end
41
-
42
- def dereferenceable_observable(opts = {})
43
- opts = opts.merge(executor: executor)
44
- Agent.new(0, opts)
45
- end
46
-
47
- def execute_dereferenceable(subject)
48
- subject.post { |value| 10 }
49
- sleep(0.1)
50
- end
51
-
52
- it_should_behave_like :dereferenceable
53
-
54
- # observable
55
-
56
- subject { Agent.new(0) }
57
-
58
- def trigger_observable(observable)
59
- observable.post { nil }
60
- sleep(0.1)
61
- end
62
-
63
- it_should_behave_like :observable
64
- end
65
-
66
- context '#initialize' do
67
-
68
- let(:executor) { ImmediateExecutor.new }
69
-
70
- it 'sets the value to the given initial state' do
71
- Agent.new(10).value.should eq 10
72
- end
73
-
74
- it 'sets the timeout to the given value' do
75
- Agent.new(0, timeout: 5).timeout.should eq 5
76
- end
77
-
78
- it 'sets the timeout to the default when nil' do
79
- Agent.new(0).timeout.should eq Agent::TIMEOUT
80
- end
81
-
82
- it 'uses the executor given with the :executor option' do
83
- executor.should_receive(:post).with(any_args).and_return(0)
84
- agent = Agent.new(0, executor: executor)
85
- agent.post { |value| 0 }
86
- end
87
-
88
- it 'uses the global operation pool when :operation is true' do
89
- Concurrent.configuration.should_receive(:global_operation_pool).and_return(executor)
90
- agent = Agent.new(0, operation: true)
91
- agent.post { |value| 0 }
92
- end
93
-
94
- it 'uses the global task pool when :task is true' do
95
- Concurrent.configuration.should_receive(:global_task_pool).and_return(executor)
96
- agent = Agent.new(0, task: true)
97
- agent.post { |value| 0 }
98
- end
99
-
100
- it 'uses the global task pool by default' do
101
- Concurrent.configuration.should_receive(:global_task_pool).and_return(executor)
102
- agent = Agent.new(0)
103
- agent.post { |value| 0 }
104
- end
105
- end
106
-
107
- context '#rescue' do
108
-
109
- it 'returns self when a block is given' do
110
- a1 = subject
111
- a2 = a1.rescue {}
112
-
113
- a2.should be a1
114
- end
115
-
116
- it 'returns self when no block is given' do
117
- a1 = subject
118
- a2 = a1.rescue
119
-
120
- a2.should be a1
121
- end
122
-
123
- it 'accepts an exception class as the first parameter' do
124
- lambda {
125
- subject.rescue(StandardError) {}
126
- }.should_not raise_error
127
- end
128
-
129
- it 'ignores rescuers without a block' do
130
- subject.rescue
131
- subject.instance_variable_get(:@rescuers).should be_empty
132
- end
133
- end
134
-
135
- context '#validate' do
136
-
137
- it 'returns self when a block is given' do
138
- a1 = subject
139
- a2 = a1.validate {}
140
-
141
- a2.should be a1
142
- end
143
-
144
- it 'returns self when no block is given' do
145
- a1 = subject
146
- a2 = a1.validate
147
-
148
- a2.should be a1
149
- end
150
-
151
- it 'ignores validators without a block' do
152
- default_validator = subject.instance_variable_get(:@validator)
153
- subject.validate
154
- subject.instance_variable_get(:@validator).should be default_validator
155
- end
156
- end
157
-
158
- context '#post' do
159
-
160
- it 'adds the given block to the queue' do
161
- executor.should_receive(:post).with(no_args).exactly(1).times
162
- subject.post { sleep(1) }
163
- subject.post { nil }
164
- subject.post { nil }
165
- sleep(0.1)
166
- subject.
167
- instance_variable_get(:@serialized_execution).
168
- instance_variable_get(:@stash).
169
- size.should eq 2
170
- end
171
-
172
- it 'does not add to the queue when no block is given' do
173
- executor.should_receive(:post).with(no_args).exactly(0).times
174
- subject.post
175
- sleep(0.1)
176
- end
177
-
178
- it 'works with ImmediateExecutor' do
179
- agent = Agent.new(0, executor: ImmediateExecutor.new)
180
- agent.post { |old| old + 1 }
181
- agent.post { |old| old + 1 }
182
- agent.value.should eq 2
183
- end
184
-
185
- end
186
-
187
- context '#await' do
188
-
189
- it 'waits until already sent updates are done' do
190
- fn = false
191
- subject.post { fn = true; sleep 0.1 }
192
- subject.await
193
- fn.should be_true
194
- end
195
-
196
- it 'does not waits until updates sent after are done' do
197
- fn = false
198
- subject.await
199
- subject.post { fn = true; sleep 0.1 }
200
- fn.should be_false
201
- end
202
-
203
- it 'does not alter the value' do
204
- subject.post { |v| v + 1 }
205
- subject.await
206
- subject.value.should eq 1
207
- end
208
-
209
- end
210
-
211
- context 'fulfillment' do
212
-
213
- it 'process each block in the queue' do
214
- @expected = []
215
- subject.post { @expected << 1 }
216
- subject.post { @expected << 2 }
217
- subject.post { @expected << 3 }
218
- sleep(0.1)
219
- @expected.sort.should eq [1, 2, 3]
220
- end
221
-
222
- it 'passes the current value to the handler' do
223
- @expected = nil
224
- Agent.new(10, executor: executor).post { |i| @expected = i }
225
- sleep(0.1)
226
- @expected.should eq 10
227
- end
228
-
229
- it 'sets the value to the handler return value on success' do
230
- subject.post { 100 }
231
- sleep(0.1)
232
- subject.value.should eq 100
233
- end
234
-
235
- it 'rejects the handler after timeout reached' do
236
- agent = Agent.new(0, timeout: 0.1, executor: executor)
237
- agent.post { sleep(1); 10 }
238
- agent.value.should eq 0
239
- end
240
- end
241
-
242
- context 'validation' do
243
-
244
- it 'processes the validator when present' do
245
- @expected = nil
246
- subject.validate { @expected = 10; true }
247
- subject.post { nil }
248
- sleep(0.1)
249
- @expected.should eq 10
250
- end
251
-
252
- it 'passes the new value to the validator' do
253
- @expected = nil
254
- subject.validate { |v| @expected = v; true }
255
- subject.post { 10 }
256
- sleep(0.1)
257
- @expected.should eq 10
258
- end
259
-
260
- it 'sets the new value when the validator returns true' do
261
- agent = Agent.new(0, executor: executor).validate { true }
262
- agent.post { 10 }
263
- sleep(0.1)
264
- agent.value.should eq 10
265
- end
266
-
267
- it 'does not change the value when the validator returns false' do
268
- agent = Agent.new(0, executor: executor).validate { false }
269
- agent.post { 10 }
270
- sleep(0.1)
271
- agent.value.should eq 0
272
- end
273
-
274
- it 'does not change the value when the validator raises an exception' do
275
- agent = Agent.new(0, executor: executor).validate { raise StandardError }
276
- agent.post { 10 }
277
- sleep(0.1)
278
- agent.value.should eq 0
279
- end
280
- end
281
-
282
- context 'rejection' do
283
-
284
- it 'calls the first exception block with a matching class' do
285
- @expected = nil
286
- subject.
287
- rescue(StandardError) { |ex| @expected = 1 }.
288
- rescue(StandardError) { |ex| @expected = 2 }.
289
- rescue(StandardError) { |ex| @expected = 3 }
290
- subject.post { raise StandardError }
291
- sleep(0.1)
292
- @expected.should eq 1
293
- end
294
-
295
- it 'matches all with a rescue with no class given' do
296
- @expected = nil
297
- subject.
298
- rescue(LoadError) { |ex| @expected = 1 }.
299
- rescue { |ex| @expected = 2 }.
300
- rescue(StandardError) { |ex| @expected = 3 }
301
- subject.post { raise NoMethodError }
302
- sleep(0.1)
303
- @expected.should eq 2
304
- end
305
-
306
- it 'searches associated rescue handlers in order' do
307
- @expected = nil
308
- subject.
309
- rescue(ArgumentError) { |ex| @expected = 1 }.
310
- rescue(LoadError) { |ex| @expected = 2 }.
311
- rescue(StandardError) { |ex| @expected = 3 }
312
- subject.post { raise ArgumentError }
313
- sleep(0.1)
314
- @expected.should eq 1
315
-
316
- @expected = nil
317
- subject.
318
- rescue(ArgumentError) { |ex| @expected = 1 }.
319
- rescue(LoadError) { |ex| @expected = 2 }.
320
- rescue(StandardError) { |ex| @expected = 3 }
321
- subject.post { raise LoadError }
322
- sleep(0.1)
323
- @expected.should eq 2
324
-
325
- @expected = nil
326
- subject.
327
- rescue(ArgumentError) { |ex| @expected = 1 }.
328
- rescue(LoadError) { |ex| @expected = 2 }.
329
- rescue(StandardError) { |ex| @expected = 3 }
330
- subject.post { raise StandardError }
331
- sleep(0.1)
332
- @expected.should eq 3
333
- end
334
-
335
- it 'passes the exception object to the matched block' do
336
- @expected = nil
337
- subject.
338
- rescue(ArgumentError) { |ex| @expected = ex }.
339
- rescue(LoadError) { |ex| @expected = ex }.
340
- rescue(StandardError) { |ex| @expected = ex }
341
- subject.post { raise StandardError }
342
- sleep(0.1)
343
- @expected.should be_a(StandardError)
344
- end
345
-
346
- it 'ignores rescuers without a block' do
347
- @expected = nil
348
- subject.
349
- rescue(StandardError).
350
- rescue(StandardError) { |ex| @expected = ex }
351
- subject.post { raise StandardError }
352
- sleep(0.1)
353
- @expected.should be_a(StandardError)
354
- end
355
-
356
- it 'supresses the exception if no rescue matches' do
357
- lambda {
358
- subject.
359
- rescue(ArgumentError) { |ex| @expected = ex }.
360
- rescue(NotImplementedError) { |ex| @expected = ex }.
361
- rescue(NoMethodError) { |ex| @expected = ex }
362
- subject.post { raise StandardError }
363
- sleep(0.1)
364
- }.should_not raise_error
365
- end
366
-
367
- it 'suppresses exceptions thrown from rescue handlers' do
368
- lambda {
369
- subject.rescue(StandardError) { raise StandardError }
370
- subject.post { raise ArgumentError }
371
- sleep(0.1)
372
- }.should_not raise_error
373
- end
374
- end
375
-
376
- context 'observation' do
377
-
378
- it 'notifies all observers when the value changes' do
379
- agent = Agent.new(0, executor: executor)
380
- agent.add_observer(observer)
381
- agent.post { 10 }
382
- sleep(0.1)
383
- observer.value.should eq 10
384
- end
385
-
386
- it 'does not notify removed observers when the value changes' do
387
- agent = Agent.new(0, executor: executor)
388
- agent.add_observer(observer)
389
- agent.delete_observer(observer)
390
- agent.post { 10 }
391
- sleep(0.1)
392
- observer.value.should be_nil
393
- end
394
-
395
- it 'does not notify observers when validation fails' do
396
- agent = Agent.new(0, executor: executor)
397
- agent.validate { false }
398
- agent.add_observer(observer)
399
- agent.post { 10 }
400
- sleep(0.1)
401
- observer.value.should be_nil
402
- end
403
-
404
- it 'does not notify observers when the handler raises an exception' do
405
- agent = Agent.new(0, executor: executor)
406
- agent.add_observer(observer)
407
- agent.post { raise StandardError }
408
- sleep(0.1)
409
- observer.value.should be_nil
410
- end
411
- end
412
-
413
- context 'clojure-like behaviour' do
414
- it 'does not block dereferencing when updating the value' do
415
- continue = IVar.new
416
- agent = Agent.new(0, executor: executor)
417
- agent.post { |old| old + continue.value }
418
- sleep 0.1
419
- Concurrent.timeout(0.2) { agent.value.should eq 0 }
420
- continue.set 1
421
- sleep 0.1
422
- end
423
-
424
- it 'does not allow to execute two updates at the same time' do
425
- agent = Agent.new(0, executor: executor)
426
- continue1 = IVar.new
427
- continue2 = IVar.new
428
- f1 = f2 = false
429
- agent.post { |old| f1 = true; old + continue1.value }
430
- agent.post { |old| f2 = true; old + continue2.value }
431
-
432
- sleep 0.1
433
- f1.should eq true
434
- f2.should eq false
435
- agent.value.should eq 0
436
-
437
- continue1.set 1
438
- sleep 0.1
439
- f1.should eq true
440
- f2.should eq true
441
- agent.value.should eq 1
442
-
443
- continue2.set 1
444
- sleep 0.1
445
- f1.should eq true
446
- f2.should eq true
447
- agent.value.should eq 2
448
- end
449
-
450
- it 'waits with sending functions to other agents until update is done'
451
- end
452
-
453
- context 'aliases' do
454
-
455
- it 'aliases #deref for #value' do
456
- Agent.new(10, executor: executor).deref.should eq 10
457
- end
458
-
459
- it 'aliases #validates for :validate' do
460
- @expected = nil
461
- subject.validates { |v| @expected = v }
462
- subject.post { 10 }
463
- sleep(0.1)
464
- @expected.should eq 10
465
- end
466
-
467
- it 'aliases #validate_with for :validate' do
468
- @expected = nil
469
- subject.validate_with { |v| @expected = v }
470
- subject.post { 10 }
471
- sleep(0.1)
472
- @expected.should eq 10
473
- end
474
-
475
- it 'aliases #validates_with for :validate' do
476
- @expected = nil
477
- subject.validates_with { |v| @expected = v }
478
- subject.post { 10 }
479
- sleep(0.1)
480
- @expected.should eq 10
481
- end
482
-
483
- it 'aliases #catch for #rescue' do
484
- @expected = nil
485
- subject.catch { @expected = true }
486
- subject.post { raise StandardError }
487
- sleep(0.1)
488
- @expected.should be_true
489
- end
490
-
491
- it 'aliases #on_error for #rescue' do
492
- @expected = nil
493
- subject.on_error { @expected = true }
494
- subject.post { raise StandardError }
495
- sleep(0.1)
496
- @expected.should be_true
497
- end
498
- end
499
- end
500
- end