concurrent-ruby 0.4.1 → 0.5.0.pre.1

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