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.
- 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
|