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.
- checksums.yaml +4 -4
- data/README.md +31 -33
- data/lib/concurrent.rb +11 -3
- data/lib/concurrent/actor.rb +29 -29
- data/lib/concurrent/agent.rb +98 -16
- data/lib/concurrent/atomic.rb +125 -0
- data/lib/concurrent/channel.rb +36 -1
- data/lib/concurrent/condition.rb +67 -0
- data/lib/concurrent/copy_on_notify_observer_set.rb +80 -0
- data/lib/concurrent/copy_on_write_observer_set.rb +94 -0
- data/lib/concurrent/count_down_latch.rb +60 -0
- data/lib/concurrent/dataflow.rb +85 -0
- data/lib/concurrent/dereferenceable.rb +69 -31
- data/lib/concurrent/event.rb +27 -21
- data/lib/concurrent/future.rb +103 -43
- data/lib/concurrent/ivar.rb +78 -0
- data/lib/concurrent/mvar.rb +154 -0
- data/lib/concurrent/obligation.rb +94 -9
- data/lib/concurrent/postable.rb +11 -9
- data/lib/concurrent/promise.rb +101 -127
- data/lib/concurrent/safe_task_executor.rb +28 -0
- data/lib/concurrent/scheduled_task.rb +60 -54
- data/lib/concurrent/stoppable.rb +2 -2
- data/lib/concurrent/supervisor.rb +36 -29
- data/lib/concurrent/thread_local_var.rb +117 -0
- data/lib/concurrent/timer_task.rb +28 -30
- data/lib/concurrent/utilities.rb +1 -1
- data/lib/concurrent/version.rb +1 -1
- data/spec/concurrent/agent_spec.rb +121 -230
- data/spec/concurrent/atomic_spec.rb +201 -0
- data/spec/concurrent/condition_spec.rb +171 -0
- data/spec/concurrent/copy_on_notify_observer_set_spec.rb +10 -0
- data/spec/concurrent/copy_on_write_observer_set_spec.rb +10 -0
- data/spec/concurrent/count_down_latch_spec.rb +125 -0
- data/spec/concurrent/dataflow_spec.rb +160 -0
- data/spec/concurrent/dereferenceable_shared.rb +145 -0
- data/spec/concurrent/event_spec.rb +44 -9
- data/spec/concurrent/fixed_thread_pool_spec.rb +0 -1
- data/spec/concurrent/future_spec.rb +184 -69
- data/spec/concurrent/ivar_spec.rb +192 -0
- data/spec/concurrent/mvar_spec.rb +380 -0
- data/spec/concurrent/obligation_spec.rb +193 -0
- data/spec/concurrent/observer_set_shared.rb +233 -0
- data/spec/concurrent/postable_shared.rb +3 -7
- data/spec/concurrent/promise_spec.rb +270 -192
- data/spec/concurrent/safe_task_executor_spec.rb +58 -0
- data/spec/concurrent/scheduled_task_spec.rb +142 -38
- data/spec/concurrent/thread_local_var_spec.rb +113 -0
- data/spec/concurrent/thread_pool_shared.rb +2 -3
- data/spec/concurrent/timer_task_spec.rb +31 -1
- data/spec/spec_helper.rb +2 -3
- data/spec/support/functions.rb +4 -0
- data/spec/support/less_than_or_equal_to_matcher.rb +5 -0
- metadata +50 -30
- data/lib/concurrent/contract.rb +0 -21
- data/lib/concurrent/event_machine_defer_proxy.rb +0 -22
- data/md/actor.md +0 -404
- data/md/agent.md +0 -142
- data/md/channel.md +0 -40
- data/md/dereferenceable.md +0 -49
- data/md/future.md +0 -125
- data/md/obligation.md +0 -32
- data/md/promise.md +0 -217
- data/md/scheduled_task.md +0 -156
- data/md/supervisor.md +0 -246
- data/md/thread_pool.md +0 -225
- data/md/timer_task.md +0 -191
- data/spec/concurrent/contract_spec.rb +0 -34
- 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.
|
18
|
+
# a crashed task thread can pose a significant problem. +TimerTask+ alleviates both problems.
|
19
19
|
#
|
20
|
-
# When a
|
21
|
-
# The
|
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
|
24
|
-
# Additionally, the
|
25
|
-
# performing logging or ancillary operations.
|
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
|
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
|
-
#
|
30
|
+
# +TimerTask+ for scheduling and running.
|
31
31
|
#
|
32
|
-
# In some cases it may be necessary for a
|
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
|
37
|
-
# the last execution is always available via the
|
38
|
-
# can be passed to the
|
39
|
-
#
|
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
|
-
#
|
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
|
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
|
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
|
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
|
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
|
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
|
152
|
+
# Default +:execution_interval+
|
155
153
|
EXECUTION_INTERVAL = 60
|
156
154
|
|
157
|
-
# Default
|
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
|
184
|
-
# block can control its own lifecycle. Necessary since
|
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
|
-
#
|
184
|
+
# +TimerTask+.
|
187
185
|
#
|
188
|
-
# @note Calls Concurrent::Dereferenceable#set_deref_options passing
|
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
|
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
|
232
|
+
# @return [Boolean] indicating whether or not the +TimerTask+ was killed
|
235
233
|
#
|
236
|
-
# @note Do not use this method unless
|
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
|
data/lib/concurrent/utilities.rb
CHANGED
@@ -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
|
18
|
+
# to the Ruby standard library +Timeout::timeout+ method.
|
19
19
|
def timeout(seconds)
|
20
20
|
|
21
21
|
thread = Thread.new do
|
data/lib/concurrent/version.rb
CHANGED
@@ -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
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
-
|
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 '
|
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 '
|
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 '
|
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 '
|
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.
|
313
|
+
agent.delete_observer(observer)
|
314
|
+
agent.post { 10 }
|
424
315
|
sleep(0.1)
|
425
|
-
observer.value.should
|
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
|