Skip to content

Commit 8ecfa2b

Browse files
committed
Merge pull request #199 from rkday/post_to_shutdown
Improve behaviour when posting to a shutdown thread pool
2 parents df20680 + 7051d2f commit 8ecfa2b

File tree

3 files changed

+83
-21
lines changed

3 files changed

+83
-21
lines changed

lib/concurrent/executor/executor.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,16 @@ module RubyExecutor
7878
def post(*args, &task)
7979
raise ArgumentError.new('no block given') unless block_given?
8080
mutex.synchronize do
81-
return false unless running?
81+
unless running?
82+
# The executor is shut down - figure out how to reject this task
83+
if self.respond_to?(:handle_overflow, true)
84+
# Reject this task in the same way we'd reject an overflow
85+
return handle_overflow(*args, &task)
86+
else
87+
# No handle_overflow method defined - just return false
88+
return false
89+
end
90+
end
8291
execute(*args, &task)
8392
true
8493
end

spec/concurrent/executor/executor_service_shared.rb

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,22 @@
1616
latch = Concurrent::CountDownLatch.new(1)
1717
subject.post{ sleep(1) }
1818
subject.shutdown
19-
subject.post{ latch.count_down }
19+
begin
20+
subject.post{ latch.count_down }
21+
rescue Concurrent::RejectedExecutionError
22+
end
2023
expect(latch.wait(0.1)).to be_falsey
2124
end
2225

23-
it 'returns false while shutting down' do
24-
subject.post{ sleep(1) }
25-
subject.shutdown
26-
expect(subject.post{ nil }).to be_falsey
27-
end
28-
2926
it 'rejects the block once shutdown' do
3027
subject.shutdown
3128
latch = Concurrent::CountDownLatch.new(1)
32-
subject.post{ sleep(1) }
33-
subject.post{ latch.count_down }
29+
begin
30+
subject.post{ latch.count_down }
31+
rescue Concurrent::RejectedExecutionError
32+
end
3433
expect(latch.wait(0.1)).to be_falsey
3534
end
36-
37-
it 'returns false once shutdown' do
38-
subject.post{ nil }
39-
subject.shutdown
40-
sleep(0.1)
41-
expect(subject.post{ nil }).to be_falsey
42-
end
4335
end
4436

4537
context '#running?' do
@@ -75,7 +67,10 @@
7567
latch2 = Concurrent::CountDownLatch.new(1)
7668
subject.post{ sleep(0.2); latch1.count_down }
7769
subject.shutdown
78-
expect(subject.post{ latch2.count_down }).to be_falsey
70+
begin
71+
expect(subject.post{ latch2.count_down }).to be_falsey
72+
rescue Concurrent::RejectedExecutionError
73+
end
7974
expect(latch1.wait(1)).to be_truthy
8075
expect(latch2.wait(0.2)).to be_falsey
8176
end
@@ -121,7 +116,10 @@
121116
subject.post{ sleep(0.1); expected.increment }
122117
subject.post{ sleep(0.1); expected.increment }
123118
subject.shutdown
124-
subject.post{ expected.increment }
119+
begin
120+
subject.post{ expected.increment }
121+
rescue Concurrent::RejectedExecutionError
122+
end
125123
subject.wait_for_termination(1)
126124
expect(expected.value).to eq(2)
127125
end
@@ -135,7 +133,10 @@
135133
subject.post{ sleep(0.1); latch.count_down }
136134
latch.wait(1)
137135
subject.kill
138-
expect(subject.post{ expected.make_true }).to be_falsey
136+
begin
137+
expect(subject.post{ expected.make_true }).to be_falsey
138+
rescue Concurrent::RejectedExecutionError
139+
end
139140
sleep(0.1)
140141
expect(expected.value).to be_falsey
141142
end
@@ -145,7 +146,10 @@
145146
sleep(0.1)
146147
subject.kill
147148
sleep(0.1)
148-
expect(subject.post{ nil }).to be_falsey
149+
begin
150+
expect(subject.post{ nil }).to be_falsey
151+
rescue Concurrent::RejectedExecutionError
152+
end
149153
end
150154
end
151155

spec/concurrent/executor/ruby_thread_pool_executor_spec.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ module Concurrent
8080
}.to raise_error(Concurrent::RejectedExecutionError)
8181
end
8282

83+
specify '#post raises an error when the executor is shutting down' do
84+
expect {
85+
subject.shutdown; subject.post{ sleep(1) }
86+
}.to raise_error(Concurrent::RejectedExecutionError)
87+
end
88+
89+
specify '#<< raises an error when the executor is shutting down' do
90+
expect {
91+
subject.shutdown; subject << proc { sleep(1) }
92+
}.to raise_error(Concurrent::RejectedExecutionError)
93+
end
94+
8395
specify 'a #post task is never executed when the queue is at capacity' do
8496
executed = Concurrent::AtomicFixnum.new(0)
8597
10.times do
@@ -134,6 +146,29 @@ module Concurrent
134146
sleep(0.1)
135147
expect(executed.value).to be < 1000
136148
end
149+
150+
specify 'a #post task is never executed when the executor is shutting down' do
151+
executed = Concurrent::AtomicFixnum.new(0)
152+
subject.shutdown
153+
subject.post{ executed.increment }
154+
sleep(0.1)
155+
expect(executed.value).to be 0
156+
end
157+
158+
specify 'a #<< task is never executed when the executor is shutting down' do
159+
executed = Concurrent::AtomicFixnum.new(0)
160+
subject.shutdown
161+
subject << proc { executed.increment }
162+
sleep(0.1)
163+
expect(executed.value).to be 0
164+
end
165+
166+
specify '#post returns false when the executor is shutting down' do
167+
executed = Concurrent::AtomicFixnum.new(0)
168+
subject.shutdown
169+
ret = subject.post{ executed.increment }
170+
expect(ret).to be false
171+
end
137172
end
138173

139174
context ':caller_runs' do
@@ -165,6 +200,20 @@ module Concurrent
165200
5.times{|i| subject.post{ latch.count_down } }
166201
latch.wait(0.1)
167202
end
203+
204+
specify '#post executes the task on the current thread when the executor is shutting down' do
205+
latch = Concurrent::CountDownLatch.new(1)
206+
subject.shutdown
207+
subject.post{ latch.count_down }
208+
latch.wait(0.1)
209+
end
210+
211+
specify '#<< executes the task on the current thread when the executor is shutting down' do
212+
latch = Concurrent::CountDownLatch.new(1)
213+
subject.shutdown
214+
subject << proc { latch.count_down }
215+
latch.wait(0.1)
216+
end
168217
end
169218
end
170219
end

0 commit comments

Comments
 (0)