concurrent-ruby 0.4.1 → 0.5.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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +31 -33
  3. data/lib/concurrent.rb +11 -3
  4. data/lib/concurrent/actor.rb +29 -29
  5. data/lib/concurrent/agent.rb +98 -16
  6. data/lib/concurrent/atomic.rb +125 -0
  7. data/lib/concurrent/channel.rb +36 -1
  8. data/lib/concurrent/condition.rb +67 -0
  9. data/lib/concurrent/copy_on_notify_observer_set.rb +80 -0
  10. data/lib/concurrent/copy_on_write_observer_set.rb +94 -0
  11. data/lib/concurrent/count_down_latch.rb +60 -0
  12. data/lib/concurrent/dataflow.rb +85 -0
  13. data/lib/concurrent/dereferenceable.rb +69 -31
  14. data/lib/concurrent/event.rb +27 -21
  15. data/lib/concurrent/future.rb +103 -43
  16. data/lib/concurrent/ivar.rb +78 -0
  17. data/lib/concurrent/mvar.rb +154 -0
  18. data/lib/concurrent/obligation.rb +94 -9
  19. data/lib/concurrent/postable.rb +11 -9
  20. data/lib/concurrent/promise.rb +101 -127
  21. data/lib/concurrent/safe_task_executor.rb +28 -0
  22. data/lib/concurrent/scheduled_task.rb +60 -54
  23. data/lib/concurrent/stoppable.rb +2 -2
  24. data/lib/concurrent/supervisor.rb +36 -29
  25. data/lib/concurrent/thread_local_var.rb +117 -0
  26. data/lib/concurrent/timer_task.rb +28 -30
  27. data/lib/concurrent/utilities.rb +1 -1
  28. data/lib/concurrent/version.rb +1 -1
  29. data/spec/concurrent/agent_spec.rb +121 -230
  30. data/spec/concurrent/atomic_spec.rb +201 -0
  31. data/spec/concurrent/condition_spec.rb +171 -0
  32. data/spec/concurrent/copy_on_notify_observer_set_spec.rb +10 -0
  33. data/spec/concurrent/copy_on_write_observer_set_spec.rb +10 -0
  34. data/spec/concurrent/count_down_latch_spec.rb +125 -0
  35. data/spec/concurrent/dataflow_spec.rb +160 -0
  36. data/spec/concurrent/dereferenceable_shared.rb +145 -0
  37. data/spec/concurrent/event_spec.rb +44 -9
  38. data/spec/concurrent/fixed_thread_pool_spec.rb +0 -1
  39. data/spec/concurrent/future_spec.rb +184 -69
  40. data/spec/concurrent/ivar_spec.rb +192 -0
  41. data/spec/concurrent/mvar_spec.rb +380 -0
  42. data/spec/concurrent/obligation_spec.rb +193 -0
  43. data/spec/concurrent/observer_set_shared.rb +233 -0
  44. data/spec/concurrent/postable_shared.rb +3 -7
  45. data/spec/concurrent/promise_spec.rb +270 -192
  46. data/spec/concurrent/safe_task_executor_spec.rb +58 -0
  47. data/spec/concurrent/scheduled_task_spec.rb +142 -38
  48. data/spec/concurrent/thread_local_var_spec.rb +113 -0
  49. data/spec/concurrent/thread_pool_shared.rb +2 -3
  50. data/spec/concurrent/timer_task_spec.rb +31 -1
  51. data/spec/spec_helper.rb +2 -3
  52. data/spec/support/functions.rb +4 -0
  53. data/spec/support/less_than_or_equal_to_matcher.rb +5 -0
  54. metadata +50 -30
  55. data/lib/concurrent/contract.rb +0 -21
  56. data/lib/concurrent/event_machine_defer_proxy.rb +0 -22
  57. data/md/actor.md +0 -404
  58. data/md/agent.md +0 -142
  59. data/md/channel.md +0 -40
  60. data/md/dereferenceable.md +0 -49
  61. data/md/future.md +0 -125
  62. data/md/obligation.md +0 -32
  63. data/md/promise.md +0 -217
  64. data/md/scheduled_task.md +0 -156
  65. data/md/supervisor.md +0 -246
  66. data/md/thread_pool.md +0 -225
  67. data/md/timer_task.md +0 -191
  68. data/spec/concurrent/contract_spec.rb +0 -34
  69. data/spec/concurrent/event_machine_defer_proxy_spec.rb +0 -240
@@ -15,39 +15,37 @@ module Concurrent
15
15
  # task itself is tightly coupled with the concurrency logic. Second, an exception in
16
16
  # raised while performing the task can cause the entire thread to abend. In a
17
17
  # long-running application where the task thread is intended to run for days/weeks/years
18
- # a crashed task thread can pose a significant problem. `TimerTask` alleviates both problems.
18
+ # a crashed task thread can pose a significant problem. +TimerTask+ alleviates both problems.
19
19
  #
20
- # When a `TimerTask` is launched it starts a thread for monitoring the execution interval.
21
- # The `TimerTask` thread does not perform the task, however. Instead, the TimerTask
20
+ # When a +TimerTask+ is launched it starts a thread for monitoring the execution interval.
21
+ # The +TimerTask+ thread does not perform the task, however. Instead, the TimerTask
22
22
  # launches the task on a separate thread. Should the task experience an unrecoverable
23
- # crash only the task thread will crash. This makes the `TimerTask` very fault tolerant
24
- # Additionally, the `TimerTask` thread can respond to the success or failure of the task,
25
- # performing logging or ancillary operations. `TimerTask` can also be configured with a
23
+ # crash only the task thread will crash. This makes the +TimerTask+ very fault tolerant
24
+ # Additionally, the +TimerTask+ thread can respond to the success or failure of the task,
25
+ # performing logging or ancillary operations. +TimerTask+ can also be configured with a
26
26
  # timeout value allowing it to kill a task that runs too long.
27
27
  #
28
- # One other advantage of `TimerTask` is it forces the bsiness logic to be completely decoupled
28
+ # One other advantage of +TimerTask+ is it forces the bsiness logic to be completely decoupled
29
29
  # from the concurrency logic. The business logic can be tested separately then passed to the
30
- # `TimerTask` for scheduling and running.
30
+ # +TimerTask+ for scheduling and running.
31
31
  #
32
- # In some cases it may be necessary for a `TimerTask` to affect its own execution cycle.
32
+ # In some cases it may be necessary for a +TimerTask+ to affect its own execution cycle.
33
33
  # To facilitate this a reference to the task object is passed into the block as a block
34
34
  # argument every time the task is executed.
35
35
  #
36
- # The `TimerTask` class includes the `Dereferenceable` mixin module so the result of
37
- # the last execution is always available via the `#value` method. Derefencing options
38
- # can be passed to the `TimerTask` during construction or at any later time using the
39
- # `#set_deref_options` method.
36
+ # The +TimerTask+ class includes the +Dereferenceable+ mixin module so the result of
37
+ # the last execution is always available via the +#value+ method. Derefencing options
38
+ # can be passed to the +TimerTask+ during construction or at any later time using the
39
+ # +#set_deref_options+ method.
40
40
  #
41
- # `TimerTask` supports notification through the Ruby standard library
41
+ # +TimerTask+ supports notification through the Ruby standard library
42
42
  # {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html Observable}
43
- # module. On execution the `TimerTask` will notify the observers
43
+ # module. On execution the +TimerTask+ will notify the observers
44
44
  # with threes arguments: time of execution, the result of the block (or nil on failure),
45
45
  # and any raised exceptions (or nil on success). If the timeout interval is exceeded
46
- # the observer will receive a `Concurrent::TimeoutError` object as the third argument.
46
+ # the observer will receive a +Concurrent::TimeoutError+ object as the third argument.
47
47
  #
48
48
  # @example Basic usage
49
- # require 'concurrent'
50
- #
51
49
  # task = Concurrent::TimerTask.new{ puts 'Boom!' }
52
50
  # task.run!
53
51
  #
@@ -59,7 +57,7 @@ module Concurrent
59
57
  #
60
58
  # task.stop #=> true
61
59
  #
62
- # @example Configuring `:execution_interval` and `:timeout_interval`
60
+ # @example Configuring +:execution_interval+ and +:timeout_interval+
63
61
  # task = Concurrent::TimerTask.new(execution_interval: 5, timeout_interval: 5) do
64
62
  # puts 'Boom!'
65
63
  # end
@@ -67,13 +65,13 @@ module Concurrent
67
65
  # task.execution_interval #=> 5
68
66
  # task.timeout_interval #=> 5
69
67
  #
70
- # @example Immediate execution with `:run_now`
68
+ # @example Immediate execution with +:run_now+
71
69
  # task = Concurrent::TimerTask.new(run_now: true){ puts 'Boom!' }
72
70
  # task.run!
73
71
  #
74
72
  # #=> 'Boom!'
75
73
  #
76
- # @example Last `#value` and `Dereferenceable` mixin
74
+ # @example Last +#value+ and +Dereferenceable+ mixin
77
75
  # task = Concurrent::TimerTask.new(
78
76
  # dup_on_deref: true,
79
77
  # execution_interval: 5
@@ -151,10 +149,10 @@ module Concurrent
151
149
  include Stoppable
152
150
  include Observable
153
151
 
154
- # Default `:execution_interval`
152
+ # Default +:execution_interval+
155
153
  EXECUTION_INTERVAL = 60
156
154
 
157
- # Default `:timeout_interval`
155
+ # Default +:timeout_interval+
158
156
  TIMEOUT_INTERVAL = 30
159
157
 
160
158
  # Number of seconds after the task completes before the task is
@@ -180,12 +178,12 @@ module Concurrent
180
178
  #
181
179
  # @yield to the block after :execution_interval seconds have passed since
182
180
  # the last yield
183
- # @yieldparam task a reference to the `TimerTask` instance so that the
184
- # block can control its own lifecycle. Necessary since `self` will
181
+ # @yieldparam task a reference to the +TimerTask+ instance so that the
182
+ # block can control its own lifecycle. Necessary since +self+ will
185
183
  # refer to the execution context of the block rather than the running
186
- # `TimerTask`.
184
+ # +TimerTask+.
187
185
  #
188
- # @note Calls Concurrent::Dereferenceable#set_deref_options passing `opts`.
186
+ # @note Calls Concurrent::Dereferenceable#set_deref_options passing +opts+.
189
187
  # All options supported by Concurrent::Dereferenceable can be set
190
188
  # during object initialization.
191
189
  #
@@ -228,12 +226,12 @@ module Concurrent
228
226
  @timeout_interval = value
229
227
  end
230
228
 
231
- # Terminate with extreme prejudice. Useful in cases where `#stop` doesn't
229
+ # Terminate with extreme prejudice. Useful in cases where +#stop+ doesn't
232
230
  # work because one of the threads becomes unresponsive.
233
231
  #
234
- # @return [Boolean] indicating whether or not the `TimerTask` was killed
232
+ # @return [Boolean] indicating whether or not the +TimerTask+ was killed
235
233
  #
236
- # @note Do not use this method unless `#stop` has failed.
234
+ # @note Do not use this method unless +#stop+ has failed.
237
235
  def kill
238
236
  return true unless running?
239
237
  mutex.synchronize do
@@ -15,7 +15,7 @@ module Concurrent
15
15
  # in the allotted number of seconds.
16
16
  #
17
17
  # @note This method is intended to be a simpler and more reliable replacement
18
- # to the Ruby standard library `Timeout::timeout` method.
18
+ # to the Ruby standard library +Timeout::timeout+ method.
19
19
  def timeout(seconds)
20
20
 
21
21
  thread = Thread.new do
@@ -1,3 +1,3 @@
1
1
  module Concurrent
2
- VERSION = '0.4.1'
2
+ VERSION = '0.5.0.pre.1'
3
3
  end
@@ -1,13 +1,11 @@
1
1
  require 'spec_helper'
2
+ require_relative 'dereferenceable_shared'
2
3
  require_relative 'uses_global_thread_pool_shared'
3
4
 
4
5
  module Concurrent
5
6
 
6
7
  describe Agent do
7
8
 
8
- let!(:thread_pool_user){ Agent }
9
- it_should_behave_like Concurrent::UsesGlobalThreadPool
10
-
11
9
  subject { Agent.new(0) }
12
10
 
13
11
  let(:observer) do
@@ -23,9 +21,30 @@ module Concurrent
23
21
  Agent.thread_pool = FixedThreadPool.new(1)
24
22
  end
25
23
 
26
- it 'includes Dereferenceable' do
27
- agent = Agent.new(10)
28
- agent.should be_a(Dereferenceable)
24
+ context 'behavior' do
25
+
26
+ # uses_global_thread_pool
27
+
28
+ let!(:thread_pool_user) { Agent }
29
+
30
+ it_should_behave_like Concurrent::UsesGlobalThreadPool
31
+
32
+ # dereferenceable
33
+
34
+ def dereferenceable_subject(value, opts = {})
35
+ Agent.new(value, opts)
36
+ end
37
+
38
+ def dereferenceable_observable(opts = {})
39
+ Agent.new(0, opts)
40
+ end
41
+
42
+ def execute_dereferenceable(subject)
43
+ subject << proc { 'value' }
44
+ sleep(0.1)
45
+ end
46
+
47
+ it_should_behave_like :dereferenceable
29
48
  end
30
49
 
31
50
  context '#initialize' do
@@ -47,19 +66,21 @@ module Concurrent
47
66
 
48
67
  it 'returns self when a block is given' do
49
68
  a1 = subject
50
- a2 = a1.rescue{}
51
- a1.object_id.should eq a2.object_id
69
+ a2 = a1.rescue {}
70
+
71
+ a2.should be a1
52
72
  end
53
73
 
54
74
  it 'returns self when no block is given' do
55
75
  a1 = subject
56
76
  a2 = a1.rescue
57
- a1.object_id.should eq a2.object_id
77
+
78
+ a2.should be a1
58
79
  end
59
80
 
60
81
  it 'accepts an exception class as the first parameter' do
61
82
  lambda {
62
- subject.rescue(StandardError){}
83
+ subject.rescue(StandardError) {}
63
84
  }.should_not raise_error
64
85
  end
65
86
 
@@ -73,37 +94,40 @@ module Concurrent
73
94
 
74
95
  it 'returns self when a block is given' do
75
96
  a1 = subject
76
- a2 = a1.validate{}
77
- a1.object_id.should eq a2.object_id
97
+ a2 = a1.validate {}
98
+
99
+ a2.should be a1
78
100
  end
79
101
 
80
102
  it 'returns self when no block is given' do
81
103
  a1 = subject
82
104
  a2 = a1.validate
83
- a1.object_id.should eq a2.object_id
105
+
106
+ a2.should be a1
84
107
  end
85
108
 
86
109
  it 'ignores validators without a block' do
110
+ default_validator = subject.instance_variable_get(:@validator)
87
111
  subject.validate
88
- subject.instance_variable_get(:@validator).should be_nil
112
+ subject.instance_variable_get(:@validator).should be default_validator
89
113
  end
90
114
  end
91
115
 
92
116
  context '#post' do
93
-
117
+
94
118
  it 'adds the given block to the queue' do
95
- Agent.thread_pool.should_receive(:post).with(no_args()).exactly(3).times
96
- subject.post{ sleep(100) }
97
- subject.post{ nil }
98
- subject.post{ nil }
119
+ Agent.thread_pool.should_receive(:post).with(no_args).exactly(3).times
120
+ subject.post { sleep(100) }
121
+ subject.post { nil }
122
+ subject.post { nil }
99
123
  sleep(0.1)
100
124
  end
101
125
 
102
126
  it 'does not add to the queue when no block is given' do
103
- Agent.thread_pool.should_receive(:post).with(no_args()).exactly(2).times
104
- subject.post{ sleep(100) }
127
+ Agent.thread_pool.should_receive(:post).with(no_args).exactly(2).times
128
+ subject.post { sleep(100) }
105
129
  subject.post
106
- subject.post{ nil }
130
+ subject.post { nil }
107
131
  sleep(0.1)
108
132
  end
109
133
  end
@@ -112,29 +136,29 @@ module Concurrent
112
136
 
113
137
  it 'process each block in the queue' do
114
138
  @expected = []
115
- subject.post{ @expected << 1 }
116
- subject.post{ @expected << 2 }
117
- subject.post{ @expected << 3 }
139
+ subject.post { @expected << 1 }
140
+ subject.post { @expected << 2 }
141
+ subject.post { @expected << 3 }
118
142
  sleep(0.1)
119
- @expected.should eq [1,2,3]
143
+ @expected.should eq [1, 2, 3]
120
144
  end
121
145
 
122
146
  it 'passes the current value to the handler' do
123
147
  @expected = nil
124
- Agent.new(10).post{|i| @expected = i }
148
+ Agent.new(10).post { |i| @expected = i }
125
149
  sleep(0.1)
126
150
  @expected.should eq 10
127
151
  end
128
152
 
129
153
  it 'sets the value to the handler return value on success' do
130
- subject.post{ 100 }
154
+ subject.post { 100 }
131
155
  sleep(0.1)
132
156
  subject.value.should eq 100
133
157
  end
134
158
 
135
159
  it 'rejects the handler after timeout reached' do
136
160
  agent = Agent.new(0, timeout: 0.1)
137
- agent.post{ sleep(1); 10 }
161
+ agent.post { sleep(1); 10 }
138
162
  agent.value.should eq 0
139
163
  end
140
164
  end
@@ -143,37 +167,37 @@ module Concurrent
143
167
 
144
168
  it 'processes the validator when present' do
145
169
  @expected = nil
146
- subject.validate{ @expected = 10; true }
147
- subject.post{ nil }
170
+ subject.validate { @expected = 10; true }
171
+ subject.post { nil }
148
172
  sleep(0.1)
149
173
  @expected.should eq 10
150
174
  end
151
175
 
152
176
  it 'passes the new value to the validator' do
153
177
  @expected = nil
154
- subject.validate{|v| @expected = v; true }
155
- subject.post{ 10 }
178
+ subject.validate { |v| @expected = v; true }
179
+ subject.post { 10 }
156
180
  sleep(0.1)
157
181
  @expected.should eq 10
158
182
  end
159
183
 
160
184
  it 'sets the new value when the validator returns true' do
161
- agent = Agent.new(0).validate{ true }
162
- agent.post{ 10 }
185
+ agent = Agent.new(0).validate { true }
186
+ agent.post { 10 }
163
187
  sleep(0.1)
164
188
  agent.value.should eq 10
165
189
  end
166
190
 
167
191
  it 'does not change the value when the validator returns false' do
168
- agent = Agent.new(0).validate{ false }
169
- agent.post{ 10 }
192
+ agent = Agent.new(0).validate { false }
193
+ agent.post { 10 }
170
194
  sleep(0.1)
171
195
  agent.value.should eq 0
172
196
  end
173
197
 
174
198
  it 'does not change the value when the validator raises an exception' do
175
- agent = Agent.new(0).validate{ raise StandardError }
176
- agent.post{ 10 }
199
+ agent = Agent.new(0).validate { raise StandardError }
200
+ agent.post { 10 }
177
201
  sleep(0.1)
178
202
  agent.value.should eq 0
179
203
  end
@@ -184,21 +208,21 @@ module Concurrent
184
208
  it 'calls the first exception block with a matching class' do
185
209
  @expected = nil
186
210
  subject.
187
- rescue(StandardError){|ex| @expected = 1 }.
188
- rescue(StandardError){|ex| @expected = 2 }.
189
- rescue(StandardError){|ex| @expected = 3 }
190
- subject.post{ raise StandardError }
191
- sleep(0.1)
211
+ rescue(StandardError) { |ex| @expected = 1 }.
212
+ rescue(StandardError) { |ex| @expected = 2 }.
213
+ rescue(StandardError) { |ex| @expected = 3 }
214
+ subject.post { raise StandardError }
215
+ sleep(0.1)
192
216
  @expected.should eq 1
193
217
  end
194
218
 
195
219
  it 'matches all with a rescue with no class given' do
196
220
  @expected = nil
197
221
  subject.
198
- rescue(LoadError){|ex| @expected = 1 }.
199
- rescue{|ex| @expected = 2 }.
200
- rescue(StandardError){|ex| @expected = 3 }
201
- subject.post{ raise NoMethodError }
222
+ rescue(LoadError) { |ex| @expected = 1 }.
223
+ rescue { |ex| @expected = 2 }.
224
+ rescue(StandardError) { |ex| @expected = 3 }
225
+ subject.post { raise NoMethodError }
202
226
  sleep(0.1)
203
227
  @expected.should eq 2
204
228
  end
@@ -206,28 +230,28 @@ module Concurrent
206
230
  it 'searches associated rescue handlers in order' do
207
231
  @expected = nil
208
232
  subject.
209
- rescue(ArgumentError){|ex| @expected = 1 }.
210
- rescue(LoadError){|ex| @expected = 2 }.
211
- rescue(StandardError){|ex| @expected = 3 }
212
- subject.post{ raise ArgumentError }
233
+ rescue(ArgumentError) { |ex| @expected = 1 }.
234
+ rescue(LoadError) { |ex| @expected = 2 }.
235
+ rescue(StandardError) { |ex| @expected = 3 }
236
+ subject.post { raise ArgumentError }
213
237
  sleep(0.1)
214
238
  @expected.should eq 1
215
239
 
216
240
  @expected = nil
217
241
  subject.
218
- rescue(ArgumentError){|ex| @expected = 1 }.
219
- rescue(LoadError){|ex| @expected = 2 }.
220
- rescue(StandardError){|ex| @expected = 3 }
221
- subject.post{ raise LoadError }
242
+ rescue(ArgumentError) { |ex| @expected = 1 }.
243
+ rescue(LoadError) { |ex| @expected = 2 }.
244
+ rescue(StandardError) { |ex| @expected = 3 }
245
+ subject.post { raise LoadError }
222
246
  sleep(0.1)
223
247
  @expected.should eq 2
224
248
 
225
249
  @expected = nil
226
250
  subject.
227
- rescue(ArgumentError){|ex| @expected = 1 }.
228
- rescue(LoadError){|ex| @expected = 2 }.
229
- rescue(StandardError){|ex| @expected = 3 }
230
- subject.post{ raise StandardError }
251
+ rescue(ArgumentError) { |ex| @expected = 1 }.
252
+ rescue(LoadError) { |ex| @expected = 2 }.
253
+ rescue(StandardError) { |ex| @expected = 3 }
254
+ subject.post { raise StandardError }
231
255
  sleep(0.1)
232
256
  @expected.should eq 3
233
257
  end
@@ -235,10 +259,10 @@ module Concurrent
235
259
  it 'passes the exception object to the matched block' do
236
260
  @expected = nil
237
261
  subject.
238
- rescue(ArgumentError){|ex| @expected = ex }.
239
- rescue(LoadError){|ex| @expected = ex }.
240
- rescue(StandardError){|ex| @expected = ex }
241
- subject.post{ raise StandardError }
262
+ rescue(ArgumentError) { |ex| @expected = ex }.
263
+ rescue(LoadError) { |ex| @expected = ex }.
264
+ rescue(StandardError) { |ex| @expected = ex }
265
+ subject.post { raise StandardError }
242
266
  sleep(0.1)
243
267
  @expected.should be_a(StandardError)
244
268
  end
@@ -247,8 +271,8 @@ module Concurrent
247
271
  @expected = nil
248
272
  subject.
249
273
  rescue(StandardError).
250
- rescue(StandardError){|ex| @expected = ex }
251
- subject.post{ raise StandardError }
274
+ rescue(StandardError) { |ex| @expected = ex }
275
+ subject.post { raise StandardError }
252
276
  sleep(0.1)
253
277
  @expected.should be_a(StandardError)
254
278
  end
@@ -256,180 +280,47 @@ module Concurrent
256
280
  it 'supresses the exception if no rescue matches' do
257
281
  lambda {
258
282
  subject.
259
- rescue(ArgumentError){|ex| @expected = ex }.
260
- rescue(NotImplementedError){|ex| @expected = ex }.
261
- rescue(NoMethodError){|ex| @expected = ex }
262
- subject.post{ raise StandardError }
283
+ rescue(ArgumentError) { |ex| @expected = ex }.
284
+ rescue(NotImplementedError) { |ex| @expected = ex }.
285
+ rescue(NoMethodError) { |ex| @expected = ex }
286
+ subject.post { raise StandardError }
263
287
  sleep(0.1)
264
288
  }.should_not raise_error
265
289
  end
266
290
 
267
- it 'supresses exceptions thrown from rescue handlers' do
291
+ it 'suppresses exceptions thrown from rescue handlers' do
268
292
  lambda {
269
- subject.rescue(StandardError){ raise StandardError }
270
- subject.post{ raise ArgumentError }
293
+ subject.rescue(StandardError) { raise StandardError }
294
+ subject.post { raise ArgumentError }
271
295
  sleep(0.1)
272
296
  }.should_not raise_error
273
297
  end
274
298
  end
275
299
 
276
- context 'Dereferenceable' do
277
-
278
- it 'defaults :dup_on_deref to false' do
279
- value = 'value'
280
- value.should_not_receive(:dup).with(any_args())
281
-
282
- agent = Agent.new(value)
283
- agent.value.should eq 'value'
284
-
285
- agent = Agent.new(value, dup_on_deref: false)
286
- agent.value.should eq 'value'
287
-
288
- agent = Agent.new(value, dup: false)
289
- agent.value.should eq 'value'
290
- end
291
-
292
- it 'calls #dup when the :dup_on_deref option is true' do
293
- value = 'value'
294
-
295
- agent = Agent.new(value, dup_on_deref: true)
296
- agent.value.object_id.should_not eq value.object_id
297
- agent.value.should eq 'value'
298
-
299
- agent = Agent.new(value, dup: true)
300
- agent.value.object_id.should_not eq value.object_id
301
- agent.value.should eq 'value'
302
- end
303
-
304
- it 'defaults :freeze_on_deref to false' do
305
- value = 'value'
306
- value.should_not_receive(:freeze).with(any_args())
307
-
308
- agent = Agent.new(value)
309
- agent.value.should eq 'value'
310
-
311
- agent = Agent.new(value, freeze_on_deref: false)
312
- agent.value.should eq 'value'
313
-
314
- agent = Agent.new(value, freeze: false)
315
- agent.value.should eq 'value'
316
- end
317
-
318
- it 'calls #freeze when the :freeze_on_deref option is true' do
319
- value = 'value'
320
-
321
- agent = Agent.new(value, freeze_on_deref: true)
322
- agent.value.should be_frozen
323
- agent.value.should eq 'value'
324
-
325
- agent = Agent.new(value, freeze: true)
326
- agent.value.should be_frozen
327
- agent.value.should eq 'value'
328
- end
329
-
330
- it 'defaults :copy_on_deref to nil' do
331
- value = 'value'
332
-
333
- agent = Agent.new(value)
334
- agent.value.object_id.should == value.object_id
335
- agent.value.should eq 'value'
336
-
337
- agent = Agent.new(value, copy_on_deref: nil)
338
- agent.value.object_id.should == value.object_id
339
- agent.value.should eq 'value'
340
-
341
- agent = Agent.new(value, copy: nil)
342
- agent.value.object_id.should == value.object_id
343
- agent.value.should eq 'value'
344
- end
345
-
346
- it 'calls the block when the :copy_on_deref option is passed a proc' do
347
- value = 'value'
348
- copy = proc{|val| 'copy' }
349
-
350
- agent = Agent.new(value, copy_on_deref: copy)
351
- agent.value.object_id.should_not == value.object_id
352
-
353
- agent = Agent.new(value, copy: copy)
354
- agent.value.object_id.should_not == value.object_id
355
- end
356
-
357
- it 'calls the :copy block first followed by #dup followed by #freeze' do
358
- value = 'value'
359
- copied = 'copied'
360
- dup = 'dup'
361
- frozen = 'frozen'
362
- copy = proc{|val| copied }
363
-
364
- copied.should_receive(:dup).with(no_args()).and_return(dup)
365
- dup.should_receive(:freeze).with(no_args()).and_return(frozen)
366
-
367
- agent = Agent.new(value, dup_on_deref: true, freeze_on_deref: true, copy_on_deref: copy)
368
- agent.value.should eq frozen
369
- end
370
-
371
- it 'does not call #dup when #dup_on_deref is set and the value is nil' do
372
- allow_message_expectations_on_nil
373
- result = nil
374
- result.should_not_receive(:dup).with(any_args())
375
- agent = Agent.new(result, dup_on_deref: true)
376
- agent.value
377
- end
378
-
379
- it 'does not call #freeze when #freeze_on_deref is set and the value is nil' do
380
- allow_message_expectations_on_nil
381
- result = nil
382
- result.should_not_receive(:freeze).with(any_args())
383
- agent = Agent.new(result, freeze_on_deref: true)
384
- agent.value
385
- end
386
-
387
- it 'does not call the #copy_on_deref block when the value is nil' do
388
- copier = proc { 42 }
389
- agent = Agent.new(nil, copy_on_deref: copier)
390
- agent.value.should be_nil
391
- end
300
+ context 'observation' do
392
301
 
393
- it 'does not lock when all options are false' do
302
+ it 'notifies all observers when the value changes' do
394
303
  agent = Agent.new(0)
395
- mutex = double('mutex')
396
- agent.stub(:mutex).and_return(mutex)
397
- mutex.should_not_receive(:synchronize)
398
- agent.value
399
- end
400
-
401
- it 'supports dereference flags with observers' do
402
- result = 'result'
403
- result.should_receive(:dup).and_return(result)
404
- result.should_receive(:freeze).and_return(result)
405
- copier = proc { result }
406
-
407
- observer = double('observer')
408
- observer.should_receive(:update).with(any_args())
409
-
410
- agent = Agent.new(nil, dup_on_deref: true, freeze_on_deref: true, copy_on_deref: copier)
411
304
  agent.add_observer(observer)
412
-
413
- agent << proc { 'original result' }
305
+ agent.post { 10 }
414
306
  sleep(0.1)
307
+ observer.value.should eq 10
415
308
  end
416
- end
417
-
418
- context 'observation' do
419
309
 
420
- it 'notifies all observers when the value changes' do
310
+ it 'does not notify removed observers when the value changes' do
421
311
  agent = Agent.new(0)
422
312
  agent.add_observer(observer)
423
- agent.post{ 10 }
313
+ agent.delete_observer(observer)
314
+ agent.post { 10 }
424
315
  sleep(0.1)
425
- observer.value.should eq 10
316
+ observer.value.should be_nil
426
317
  end
427
318
 
428
319
  it 'does not notify observers when validation fails' do
429
320
  agent = Agent.new(0)
430
- agent.validate{ false }
321
+ agent.validate { false }
431
322
  agent.add_observer(observer)
432
- agent.post{ 10 }
323
+ agent.post { 10 }
433
324
  sleep(0.1)
434
325
  observer.value.should be_nil
435
326
  end
@@ -437,7 +328,7 @@ module Concurrent
437
328
  it 'does not notify observers when the handler raises an exception' do
438
329
  agent = Agent.new(0)
439
330
  agent.add_observer(observer)
440
- agent.post{ raise StandardError }
331
+ agent.post { raise StandardError }
441
332
  sleep(0.1)
442
333
  observer.value.should be_nil
443
334
  end
@@ -451,40 +342,40 @@ module Concurrent
451
342
 
452
343
  it 'aliases #validates for :validate' do
453
344
  @expected = nil
454
- subject.validates{|v| @expected = v }
455
- subject.post{ 10 }
345
+ subject.validates { |v| @expected = v }
346
+ subject.post { 10 }
456
347
  sleep(0.1)
457
348
  @expected.should eq 10
458
349
  end
459
-
350
+
460
351
  it 'aliases #validate_with for :validate' do
461
352
  @expected = nil
462
- subject.validate_with{|v| @expected = v }
463
- subject.post{ 10 }
353
+ subject.validate_with { |v| @expected = v }
354
+ subject.post { 10 }
464
355
  sleep(0.1)
465
356
  @expected.should eq 10
466
357
  end
467
358
 
468
359
  it 'aliases #validates_with for :validate' do
469
360
  @expected = nil
470
- subject.validates_with{|v| @expected = v }
471
- subject.post{ 10 }
361
+ subject.validates_with { |v| @expected = v }
362
+ subject.post { 10 }
472
363
  sleep(0.1)
473
364
  @expected.should eq 10
474
365
  end
475
366
 
476
367
  it 'aliases #catch for #rescue' do
477
368
  @expected = nil
478
- subject.catch{ @expected = true }
479
- subject.post{ raise StandardError }
369
+ subject.catch { @expected = true }
370
+ subject.post { raise StandardError }
480
371
  sleep(0.1)
481
372
  @expected.should be_true
482
373
  end
483
374
 
484
375
  it 'aliases #on_error for #rescue' do
485
376
  @expected = nil
486
- subject.on_error{ @expected = true }
487
- subject.post{ raise StandardError }
377
+ subject.on_error { @expected = true }
378
+ subject.post { raise StandardError }
488
379
  sleep(0.1)
489
380
  @expected.should be_true
490
381
  end
@@ -492,7 +383,7 @@ module Concurrent
492
383
  it 'aliases #add_watch for #add_observer' do
493
384
  agent = Agent.new(0)
494
385
  agent.add_watch(observer)
495
- agent.post{ 10 }
386
+ agent.post { 10 }
496
387
  sleep(0.1)
497
388
  observer.value.should eq 10
498
389
  end
@@ -509,7 +400,7 @@ module Concurrent
509
400
  counter = Concurrent::Agent.new(0)
510
401
 
511
402
  count.times do |i|
512
- counter.post{|value| value + 1 }
403
+ counter.post { |value| value + 1 }
513
404
  end
514
405
 
515
406
  sleep(0.1) until counter.value == count