concurrent-ruby 0.3.0.pre.1 → 0.3.0.pre.2
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 +10 -33
- data/lib/concurrent.rb +5 -11
- data/lib/concurrent/{channel.rb → actor.rb} +14 -18
- data/lib/concurrent/agent.rb +5 -4
- data/lib/concurrent/cached_thread_pool.rb +116 -25
- data/lib/concurrent/cached_thread_pool/worker.rb +91 -0
- data/lib/concurrent/event.rb +13 -14
- data/lib/concurrent/event_machine_defer_proxy.rb +0 -1
- data/lib/concurrent/executor.rb +0 -1
- data/lib/concurrent/fixed_thread_pool.rb +111 -14
- data/lib/concurrent/fixed_thread_pool/worker.rb +54 -0
- data/lib/concurrent/future.rb +0 -2
- data/lib/concurrent/global_thread_pool.rb +21 -3
- data/lib/concurrent/goroutine.rb +1 -5
- data/lib/concurrent/obligation.rb +0 -19
- data/lib/concurrent/promise.rb +2 -5
- data/lib/concurrent/runnable.rb +2 -8
- data/lib/concurrent/supervisor.rb +9 -4
- data/lib/concurrent/utilities.rb +24 -0
- data/lib/concurrent/version.rb +1 -1
- data/md/agent.md +3 -3
- data/md/future.md +4 -4
- data/md/promise.md +15 -25
- data/md/thread_pool.md +9 -8
- data/spec/concurrent/actor_spec.rb +377 -0
- data/spec/concurrent/agent_spec.rb +2 -1
- data/spec/concurrent/cached_thread_pool_spec.rb +19 -29
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +1 -1
- data/spec/concurrent/event_spec.rb +1 -1
- data/spec/concurrent/executor_spec.rb +0 -8
- data/spec/concurrent/fixed_thread_pool_spec.rb +27 -16
- data/spec/concurrent/future_spec.rb +0 -13
- data/spec/concurrent/global_thread_pool_spec.rb +73 -0
- data/spec/concurrent/goroutine_spec.rb +0 -15
- data/spec/concurrent/obligation_shared.rb +1 -38
- data/spec/concurrent/promise_spec.rb +28 -47
- data/spec/concurrent/supervisor_spec.rb +1 -2
- data/spec/concurrent/thread_pool_shared.rb +28 -7
- data/spec/concurrent/utilities_spec.rb +50 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/functions.rb +17 -0
- metadata +12 -27
- data/lib/concurrent/functions.rb +0 -105
- data/lib/concurrent/null_thread_pool.rb +0 -25
- data/lib/concurrent/thread_pool.rb +0 -149
- data/md/reactor.md +0 -32
- data/spec/concurrent/channel_spec.rb +0 -446
- data/spec/concurrent/functions_spec.rb +0 -197
- data/spec/concurrent/null_thread_pool_spec.rb +0 -78
@@ -240,9 +240,10 @@ module Concurrent
|
|
240
240
|
|
241
241
|
it 'ignores rescuers without a block' do
|
242
242
|
@expected = nil
|
243
|
-
|
243
|
+
subject.
|
244
244
|
rescue(StandardError).
|
245
245
|
rescue(StandardError){|ex| @expected = ex }
|
246
|
+
subject.post{ raise StandardError }
|
246
247
|
sleep(0.1)
|
247
248
|
@expected.should be_a(StandardError)
|
248
249
|
end
|
@@ -29,39 +29,39 @@ module Concurrent
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
context '#
|
32
|
+
context '#length' do
|
33
33
|
|
34
34
|
it 'returns zero for a new thread pool' do
|
35
|
-
subject.
|
35
|
+
subject.length.should eq 0
|
36
36
|
end
|
37
37
|
|
38
|
-
it 'returns the
|
38
|
+
it 'returns the length of the subject when running' do
|
39
39
|
5.times{ sleep(0.1); subject << proc{ sleep(1) } }
|
40
|
-
subject.
|
40
|
+
subject.length.should eq 5
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'returns zero once shut down' do
|
44
44
|
subject.shutdown
|
45
|
-
subject.
|
45
|
+
subject.length.should eq 0
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
context 'worker creation and caching' do
|
50
50
|
|
51
51
|
it 'creates new workers when there are none available' do
|
52
|
-
subject.
|
52
|
+
subject.length.should eq 0
|
53
53
|
5.times{ sleep(0.1); subject << proc{ sleep } }
|
54
54
|
sleep(1)
|
55
|
-
subject.
|
55
|
+
subject.length.should eq 5
|
56
56
|
end
|
57
57
|
|
58
58
|
it 'uses existing idle threads' do
|
59
59
|
5.times{ subject << proc{ sleep(0.1) } }
|
60
60
|
sleep(1)
|
61
|
-
subject.
|
61
|
+
subject.length.should >= 5
|
62
62
|
3.times{ subject << proc{ sleep } }
|
63
63
|
sleep(0.1)
|
64
|
-
subject.
|
64
|
+
subject.length.should >= 5
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'never creates more than :max_threads threads' do
|
@@ -79,32 +79,22 @@ module Concurrent
|
|
79
79
|
|
80
80
|
context 'garbage collection' do
|
81
81
|
|
82
|
-
subject{ CachedThreadPool.new(gc_interval: 1, idletime:
|
82
|
+
subject{ CachedThreadPool.new(gc_interval: 1, idletime: 1) }
|
83
83
|
|
84
84
|
it 'removes from pool any thread that has been idle too long' do
|
85
|
+
3.times { subject << proc{ sleep(0.1) } }
|
86
|
+
subject.length.should eq 3
|
87
|
+
sleep(2)
|
85
88
|
subject << proc{ nil }
|
86
|
-
subject.
|
87
|
-
sleep(1.5)
|
88
|
-
subject.size.should eq 0
|
89
|
+
subject.length.should < 3
|
89
90
|
end
|
90
91
|
|
91
92
|
it 'removed from pool any dead thread' do
|
92
|
-
subject << proc{ raise
|
93
|
-
subject.
|
94
|
-
sleep(
|
95
|
-
subject
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
context '#status' do
|
100
|
-
|
101
|
-
it 'returns an empty collection when the pool is empty' do
|
102
|
-
subject.status.should be_empty
|
103
|
-
end
|
104
|
-
|
105
|
-
it 'returns one status object for each thread in the pool' do
|
106
|
-
3.times{ sleep(0.1); subject << proc{ sleep(0.5) } }
|
107
|
-
subject.status.length.should eq 3
|
93
|
+
3.times { subject << proc{ sleep(0.1); raise Exception } }
|
94
|
+
subject.length.should == 3
|
95
|
+
sleep(2)
|
96
|
+
subject << proc{ nil }
|
97
|
+
subject.length.should < 3
|
108
98
|
end
|
109
99
|
end
|
110
100
|
end
|
@@ -171,14 +171,6 @@ module Concurrent
|
|
171
171
|
@expected.should eq args
|
172
172
|
end
|
173
173
|
|
174
|
-
it 'supresses exceptions thrown by the execution block' do
|
175
|
-
lambda {
|
176
|
-
@subject = Executor.new('Foo', execution_interval: 0.5) { raise StandardError }
|
177
|
-
@thread = Thread.new { @subject.run }
|
178
|
-
sleep(1)
|
179
|
-
}.should_not raise_error
|
180
|
-
end
|
181
|
-
|
182
174
|
it 'kills the worker thread if the timeout is reached' do
|
183
175
|
# the after(:each) block will trigger this expectation
|
184
176
|
Thread.should_receive(:kill).at_least(1).with(any_args())
|
@@ -16,46 +16,46 @@ module Concurrent
|
|
16
16
|
|
17
17
|
context '#initialize' do
|
18
18
|
|
19
|
-
it 'raises an exception when the pool
|
19
|
+
it 'raises an exception when the pool length is less than one' do
|
20
20
|
lambda {
|
21
21
|
FixedThreadPool.new(0)
|
22
22
|
}.should raise_error(ArgumentError)
|
23
23
|
end
|
24
24
|
|
25
|
-
it 'raises an exception when the pool
|
25
|
+
it 'raises an exception when the pool length is greater than MAX_POOL_SIZE' do
|
26
26
|
lambda {
|
27
27
|
FixedThreadPool.new(FixedThreadPool::MAX_POOL_SIZE + 1)
|
28
28
|
}.should raise_error(ArgumentError)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
context '#
|
32
|
+
context '#length' do
|
33
33
|
|
34
|
-
let(:
|
35
|
-
subject { FixedThreadPool.new(
|
34
|
+
let(:pool_length) { 3 }
|
35
|
+
subject { FixedThreadPool.new(pool_length) }
|
36
36
|
|
37
37
|
it 'returns zero on start' do
|
38
38
|
subject.shutdown
|
39
|
-
subject.
|
39
|
+
subject.length.should eq 0
|
40
40
|
end
|
41
41
|
|
42
|
-
it 'returns the
|
43
|
-
|
42
|
+
it 'returns the length of the pool when running' do
|
43
|
+
pool_length.times do |i|
|
44
44
|
subject.post{ sleep }
|
45
45
|
sleep(0.1)
|
46
|
-
subject.
|
46
|
+
subject.length.should eq pool_length
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'returns zero while shutting down' do
|
51
51
|
subject.post{ sleep(1) }
|
52
52
|
subject.shutdown
|
53
|
-
subject.
|
53
|
+
subject.length.should eq 0
|
54
54
|
end
|
55
55
|
|
56
56
|
it 'returns zero once shut down' do
|
57
57
|
subject.shutdown
|
58
|
-
subject.
|
58
|
+
subject.length.should eq 0
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -63,10 +63,10 @@ module Concurrent
|
|
63
63
|
|
64
64
|
it 'creates new workers when there are none available' do
|
65
65
|
pool = FixedThreadPool.new(5)
|
66
|
-
pool.
|
66
|
+
pool.length.should eq 0
|
67
67
|
5.times{ sleep(0.1); pool << proc{ sleep } }
|
68
68
|
sleep(0.1)
|
69
|
-
pool.
|
69
|
+
pool.length.should eq 5
|
70
70
|
pool.kill
|
71
71
|
end
|
72
72
|
|
@@ -77,16 +77,27 @@ module Concurrent
|
|
77
77
|
pool.length.should eq 5
|
78
78
|
pool.kill
|
79
79
|
end
|
80
|
+
|
81
|
+
it 'creates new threads when garbage collecting' do
|
82
|
+
pool = FixedThreadPool.new(5)
|
83
|
+
pool.length.should == 0
|
84
|
+
pool << proc { sleep }
|
85
|
+
sleep(0.1)
|
86
|
+
pool.length.should == 5
|
87
|
+
pool.instance_variable_set(:@max_threads, 25)
|
88
|
+
pool << proc { sleep }
|
89
|
+
pool.length.should == 25
|
90
|
+
end
|
80
91
|
end
|
81
92
|
|
82
93
|
context 'exception handling' do
|
83
94
|
|
84
95
|
it 'restarts threads that experience exception' do
|
96
|
+
pending
|
85
97
|
pool = FixedThreadPool.new(5)
|
86
98
|
5.times{ pool << proc{ raise StandardError } }
|
87
|
-
sleep(
|
88
|
-
pool.
|
89
|
-
pool.status.should_not include(nil)
|
99
|
+
sleep(1)
|
100
|
+
pool.length.should eq 5
|
90
101
|
end
|
91
102
|
end
|
92
103
|
end
|
@@ -30,17 +30,6 @@ module Concurrent
|
|
30
30
|
|
31
31
|
it_should_behave_like Concurrent::Obligation
|
32
32
|
|
33
|
-
context 'behavior' do
|
34
|
-
|
35
|
-
it 'implements :future behavior' do
|
36
|
-
lambda {
|
37
|
-
Future.new{ nil }
|
38
|
-
}.should_not raise_error
|
39
|
-
|
40
|
-
Future.new{ nil }.behaves_as?(:future).should be_true
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
33
|
context '#initialize' do
|
45
34
|
|
46
35
|
it 'spawns a new thread when a block is given' do
|
@@ -151,7 +140,6 @@ module Concurrent
|
|
151
140
|
sleep(0.1)
|
152
141
|
future.value.should == 42
|
153
142
|
future.add_observer(observer)
|
154
|
-
observer.value.should be_nil
|
155
143
|
sleep(0.1)
|
156
144
|
observer.value.should == 42
|
157
145
|
end
|
@@ -161,7 +149,6 @@ module Concurrent
|
|
161
149
|
sleep(0.1)
|
162
150
|
future.reason.should be_a(StandardError)
|
163
151
|
future.add_observer(observer)
|
164
|
-
observer.value.should be_nil
|
165
152
|
sleep(0.1)
|
166
153
|
observer.reason.should be_a(StandardError)
|
167
154
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'concurrent/goroutine'
|
2
3
|
require_relative 'uses_global_thread_pool_shared'
|
3
4
|
|
4
5
|
module Concurrent
|
@@ -8,4 +9,76 @@ module Concurrent
|
|
8
9
|
let!(:thread_pool_user){ Class.new{ include UsesGlobalThreadPool } }
|
9
10
|
it_should_behave_like Concurrent::UsesGlobalThreadPool
|
10
11
|
end
|
12
|
+
|
13
|
+
describe NullThreadPool do
|
14
|
+
|
15
|
+
subject { NullThreadPool.new }
|
16
|
+
|
17
|
+
after(:all) do
|
18
|
+
$GLOBAL_THREAD_POOL = FixedThreadPool.new(1)
|
19
|
+
end
|
20
|
+
|
21
|
+
context '#post' do
|
22
|
+
|
23
|
+
it 'creates a new thread for a call without arguments' do
|
24
|
+
thread = Thread.new{ nil }
|
25
|
+
Thread.should_receive(:new).with(no_args()).and_return(thread)
|
26
|
+
$GLOBAL_THREAD_POOL.should_not_receive(:post).with(any_args())
|
27
|
+
subject.post{ nil }
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'executes a call without arguments' do
|
31
|
+
@expected = false
|
32
|
+
subject.post{ @expected = true }
|
33
|
+
sleep(0.1)
|
34
|
+
@expected.should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'creates a new thread for a call with arguments' do
|
38
|
+
thread = Thread.new{ nil }
|
39
|
+
Thread.should_receive(:new).with(1,2,3).and_return(thread)
|
40
|
+
$GLOBAL_THREAD_POOL.should_not_receive(:post).with(any_args())
|
41
|
+
subject.post(1,2,3){ nil }
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'executes a call with one argument' do
|
45
|
+
@expected = 0
|
46
|
+
subject.post(1){|one| @expected = one }
|
47
|
+
sleep(0.1)
|
48
|
+
@expected.should == 1
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'executes a call with multiple arguments' do
|
52
|
+
@expected = nil
|
53
|
+
subject.post(1,2,3,4,5){|*args| @expected = args }
|
54
|
+
sleep(0.1)
|
55
|
+
@expected.should eq [1,2,3,4,5]
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'aliases #<<' do
|
59
|
+
thread = Thread.new{ nil }
|
60
|
+
Thread.should_receive(:new).with(no_args()).and_return(thread)
|
61
|
+
$GLOBAL_THREAD_POOL.should_not_receive(:post).with(any_args())
|
62
|
+
subject << proc{ nil }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'operation' do
|
67
|
+
|
68
|
+
context 'goroutine' do
|
69
|
+
|
70
|
+
it 'gets a new thread' do
|
71
|
+
$GLOBAL_THREAD_POOL = subject
|
72
|
+
|
73
|
+
t = Thread.new{ nil }
|
74
|
+
|
75
|
+
Thread.should_receive(:new).with(no_args()).and_return(t)
|
76
|
+
go{ nil }
|
77
|
+
|
78
|
+
Thread.should_receive(:new).with(1,2,3).and_return(t)
|
79
|
+
go(1,2,3){ nil }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
11
84
|
end
|
@@ -48,20 +48,5 @@ module Concurrent
|
|
48
48
|
sleep(0.1)
|
49
49
|
@expected.should eq [1,2,3]
|
50
50
|
end
|
51
|
-
|
52
|
-
it 'accepts an alternate thread pool as the first argument' do
|
53
|
-
pool = Concurrent::FixedThreadPool.new(2)
|
54
|
-
pool.should_receive(:post).with(no_args())
|
55
|
-
go(pool){ sleep(0.1) }
|
56
|
-
sleep(0.2)
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'passes all other arguments to the block when a thread pool is given' do
|
60
|
-
@expected = nil
|
61
|
-
pool = Concurrent::FixedThreadPool.new(2)
|
62
|
-
go(pool, 1, 2, 3){|a, b, c| @expected = [c, b, a] }
|
63
|
-
sleep(0.1)
|
64
|
-
@expected.should eq [3, 2, 1]
|
65
|
-
end
|
66
51
|
end
|
67
52
|
end
|
@@ -38,7 +38,7 @@ share_examples_for Concurrent::Obligation do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'returns immediately when timeout is zero' do
|
41
|
-
|
41
|
+
Concurrent.should_not_receive(:timeout).with(any_args())
|
42
42
|
f = pending_subject
|
43
43
|
f.value(0).should be_nil
|
44
44
|
f.should be_pending
|
@@ -87,41 +87,4 @@ share_examples_for Concurrent::Obligation do
|
|
87
87
|
rejected_subject.reason.to_s.should =~ /#{rejected_reason}/
|
88
88
|
end
|
89
89
|
end
|
90
|
-
|
91
|
-
context 'Kernel aliases' do
|
92
|
-
|
93
|
-
it 'aliases Kernel#deref for #deref' do
|
94
|
-
deref(fulfilled_subject).should eq fulfilled_value
|
95
|
-
deref(fulfilled_subject, 0).should eq fulfilled_value
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'aliases Kernel#pending? for #pending?' do
|
99
|
-
#NOTE: was structured like others but was incorrectly failing
|
100
|
-
# on fulfilled_subject
|
101
|
-
fulfilled_subject.should_receive(:pending?).once
|
102
|
-
pending?(fulfilled_subject)
|
103
|
-
pending_subject.should_receive(:pending?).once
|
104
|
-
pending?(pending_subject)
|
105
|
-
rejected_subject.should_receive(:pending?).once
|
106
|
-
pending?(rejected_subject)
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'aliases Kernel#fulfilled? for #fulfilled?' do
|
110
|
-
fulfilled?(fulfilled_subject).should be_true
|
111
|
-
fulfilled?(pending_subject).should be_false
|
112
|
-
fulfilled?(rejected_subject).should be_false
|
113
|
-
end
|
114
|
-
|
115
|
-
it 'aliases Kernel#realized? for #realized?' do
|
116
|
-
realized?(fulfilled_subject).should be_true
|
117
|
-
realized?(pending_subject).should be_false
|
118
|
-
realized?(rejected_subject).should be_false
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'aliases Kernel#rejected? for #rejected?' do
|
122
|
-
rejected?(rejected_subject).should be_true
|
123
|
-
rejected?(fulfilled_subject).should be_false
|
124
|
-
rejected?(pending_subject).should be_false
|
125
|
-
end
|
126
|
-
end
|
127
90
|
end
|
@@ -31,25 +31,6 @@ module Concurrent
|
|
31
31
|
|
32
32
|
it_should_behave_like Concurrent::Obligation
|
33
33
|
|
34
|
-
context 'behavior' do
|
35
|
-
|
36
|
-
it 'implements :promise behavior' do
|
37
|
-
lambda {
|
38
|
-
Promise.new{ nil }
|
39
|
-
}.should_not raise_error
|
40
|
-
|
41
|
-
Promise.new{ nil }.behaves_as?(:promise).should be_true
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'implements :future behavior' do
|
45
|
-
lambda {
|
46
|
-
Promise.new{ nil }
|
47
|
-
}.should_not raise_error
|
48
|
-
|
49
|
-
Promise.new{ nil }.behaves_as?(:future).should be_true
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
34
|
context '#then' do
|
54
35
|
|
55
36
|
it 'returns a new Promise when :pending' do
|
@@ -154,8 +135,8 @@ module Concurrent
|
|
154
135
|
context 'rejection' do
|
155
136
|
|
156
137
|
it 'sets the promise reason the error object on exception' do
|
157
|
-
p = Promise.new{ raise StandardError.new('Boom!') }
|
158
|
-
sleep(0.
|
138
|
+
p = Promise.new{ sleep(0.1); raise StandardError.new('Boom!') }
|
139
|
+
sleep(0.2)
|
159
140
|
p.reason.should be_a(StandardError)
|
160
141
|
p.reason.should.to_s =~ /Boom!/
|
161
142
|
end
|
@@ -167,36 +148,36 @@ module Concurrent
|
|
167
148
|
end
|
168
149
|
|
169
150
|
it 'recursively rejects all children' do
|
170
|
-
p = Promise.new{ raise StandardError.new('Boom!') }
|
151
|
+
p = Promise.new{ sleep(0.1); raise StandardError.new('Boom!') }
|
171
152
|
promises = 10.times.collect{ p.then{ true } }
|
172
|
-
sleep(0.
|
153
|
+
sleep(0.5)
|
173
154
|
10.times.each{|i| promises[i].should be_rejected }
|
174
155
|
end
|
175
156
|
|
176
157
|
it 'skips processing rejected promises' do
|
177
|
-
p = Promise.new{ raise StandardError.new('Boom!') }
|
158
|
+
p = Promise.new{ sleep(0.1); raise StandardError.new('Boom!') }
|
178
159
|
promises = 3.times.collect{ p.then{ true } }
|
179
|
-
sleep(0.
|
160
|
+
sleep(0.5)
|
180
161
|
promises.each{|p| p.value.should_not be_true }
|
181
162
|
end
|
182
163
|
|
183
164
|
it 'calls the first exception block with a matching class' do
|
184
165
|
@expected = nil
|
185
|
-
Promise.new{ raise StandardError }.
|
166
|
+
Promise.new{ sleep(0.1); raise StandardError }.
|
186
167
|
rescue(StandardError){|ex| @expected = 1 }.
|
187
168
|
rescue(StandardError){|ex| @expected = 2 }.
|
188
169
|
rescue(StandardError){|ex| @expected = 3 }
|
189
|
-
sleep(0.
|
170
|
+
sleep(0.2)
|
190
171
|
@expected.should eq 1
|
191
172
|
end
|
192
173
|
|
193
174
|
it 'matches all with a rescue with no class given' do
|
194
175
|
@expected = nil
|
195
|
-
Promise.new{ raise NoMethodError }.
|
176
|
+
Promise.new{ sleep(0.1); raise NoMethodError }.
|
196
177
|
rescue(LoadError){|ex| @expected = 1 }.
|
197
178
|
rescue{|ex| @expected = 2 }.
|
198
179
|
rescue(StandardError){|ex| @expected = 3 }
|
199
|
-
sleep(0.
|
180
|
+
sleep(0.2)
|
200
181
|
@expected.should eq 2
|
201
182
|
end
|
202
183
|
|
@@ -204,84 +185,84 @@ module Concurrent
|
|
204
185
|
Promise.thread_pool = CachedThreadPool.new
|
205
186
|
|
206
187
|
@expected = nil
|
207
|
-
Promise.new{ raise ArgumentError }.
|
188
|
+
Promise.new{ sleep(0.1); raise ArgumentError }.
|
208
189
|
rescue(ArgumentError){|ex| @expected = 1 }.
|
209
190
|
rescue(LoadError){|ex| @expected = 2 }.
|
210
191
|
rescue(StandardError){|ex| @expected = 3 }
|
211
|
-
sleep(0.
|
192
|
+
sleep(0.2)
|
212
193
|
@expected.should eq 1
|
213
194
|
|
214
195
|
@expected = nil
|
215
|
-
Promise.new{ raise LoadError }.
|
196
|
+
Promise.new{ sleep(0.1); raise LoadError }.
|
216
197
|
rescue(ArgumentError){|ex| @expected = 1 }.
|
217
198
|
rescue(LoadError){|ex| @expected = 2 }.
|
218
199
|
rescue(StandardError){|ex| @expected = 3 }
|
219
|
-
sleep(0.
|
200
|
+
sleep(0.2)
|
220
201
|
@expected.should eq 2
|
221
202
|
|
222
203
|
@expected = nil
|
223
|
-
Promise.new{ raise StandardError }.
|
204
|
+
Promise.new{ sleep(0.1); raise StandardError }.
|
224
205
|
rescue(ArgumentError){|ex| @expected = 1 }.
|
225
206
|
rescue(LoadError){|ex| @expected = 2 }.
|
226
207
|
rescue(StandardError){|ex| @expected = 3 }
|
227
|
-
sleep(0.
|
208
|
+
sleep(0.2)
|
228
209
|
@expected.should eq 3
|
229
210
|
end
|
230
211
|
|
231
212
|
it 'passes the exception object to the matched block' do
|
232
213
|
@expected = nil
|
233
|
-
Promise.new{ raise StandardError }.
|
214
|
+
Promise.new{ sleep(0.1); raise StandardError }.
|
234
215
|
rescue(ArgumentError){|ex| @expected = ex }.
|
235
216
|
rescue(LoadError){|ex| @expected = ex }.
|
236
217
|
rescue(StandardError){|ex| @expected = ex }
|
237
|
-
sleep(0.
|
218
|
+
sleep(0.2)
|
238
219
|
@expected.should be_a(StandardError)
|
239
220
|
end
|
240
221
|
|
241
222
|
it 'ignores rescuers without a block' do
|
242
223
|
@expected = nil
|
243
|
-
Promise.new{ raise StandardError }.
|
224
|
+
Promise.new{ sleep(0.1); raise StandardError }.
|
244
225
|
rescue(StandardError).
|
245
226
|
rescue(StandardError){|ex| @expected = ex }
|
246
|
-
sleep(0.
|
227
|
+
sleep(0.2)
|
247
228
|
@expected.should be_a(StandardError)
|
248
229
|
end
|
249
230
|
|
250
231
|
it 'supresses the exception if no rescue matches' do
|
251
232
|
lambda {
|
252
|
-
Promise.new{ raise StandardError }.
|
233
|
+
Promise.new{ sleep(0.1); raise StandardError }.
|
253
234
|
rescue(ArgumentError){|ex| @expected = ex }.
|
254
235
|
rescue(NotImplementedError){|ex| @expected = ex }.
|
255
236
|
rescue(NoMethodError){|ex| @expected = ex }
|
256
|
-
sleep(0.
|
237
|
+
sleep(0.2)
|
257
238
|
}.should_not raise_error
|
258
239
|
end
|
259
240
|
|
260
241
|
it 'supresses exceptions thrown from rescue handlers' do
|
261
242
|
lambda {
|
262
|
-
Promise.new{ raise ArgumentError }.
|
243
|
+
Promise.new{ sleep(0.1); raise ArgumentError }.
|
263
244
|
rescue(StandardError){ raise StandardError }
|
264
|
-
sleep(0.
|
245
|
+
sleep(0.2)
|
265
246
|
}.should_not raise_error
|
266
247
|
end
|
267
248
|
|
268
249
|
it 'calls matching rescue handlers on all children' do
|
269
250
|
@expected = []
|
270
|
-
Promise.new{ raise StandardError }.
|
251
|
+
Promise.new{ sleep(0.1); raise StandardError }.
|
271
252
|
then{ sleep(0.1) }.rescue{ @expected << 'Boom!' }.
|
272
253
|
then{ sleep(0.1) }.rescue{ @expected << 'Boom!' }.
|
273
254
|
then{ sleep(0.1) }.rescue{ @expected << 'Boom!' }.
|
274
255
|
then{ sleep(0.1) }.rescue{ @expected << 'Boom!' }.
|
275
256
|
then{ sleep(0.1) }.rescue{ @expected << 'Boom!' }
|
276
|
-
sleep(
|
257
|
+
sleep(1)
|
277
258
|
|
278
259
|
@expected.length.should eq 5
|
279
260
|
end
|
280
261
|
|
281
262
|
it 'matches a rescue handler added after rejection' do
|
282
263
|
@expected = false
|
283
|
-
p = Promise.new{ raise StandardError }
|
284
|
-
sleep(0.
|
264
|
+
p = Promise.new{ sleep(0.1); raise StandardError }
|
265
|
+
sleep(0.2)
|
285
266
|
p.rescue(StandardError){ @expected = true }
|
286
267
|
@expected.should be_true
|
287
268
|
end
|