diff --git a/lib/async/pool/controller.rb b/lib/async/pool/controller.rb index fc2bea2..3ed1d43 100644 --- a/lib/async/pool/controller.rb +++ b/lib/async/pool/controller.rb @@ -260,7 +260,13 @@ def start_gardener end ensure @gardener = nil - self.close + + # During cancellation, busy resources will never be released, + # so force-retire everything instead of draining gracefully. + while (resource, _usage = @resources.first) + retire(resource) + end + @available.clear end end diff --git a/test/async/pool/controller.rb b/test/async/pool/controller.rb index 979bde0..d52adf4 100644 --- a/test/async/pool/controller.rb +++ b/test/async/pool/controller.rb @@ -301,6 +301,26 @@ expect(events).to be == [:acquire, :close, :release, :closed] end + + it "does not deadlock when Sync scope exits with unreleased resources" do + thread = Thread.new do + Sync do + policy = proc{|pool| pool.prune(0)} + pool = subject.new(Async::Pool::Resource, policy: policy) + + # Acquire a resource (starts the gardener) but don't release it + pool.acquire + + # Let gardener start and enter its wait loop + sleep 0.05 + end + end + + result = thread.join(5) + thread.kill if result.nil? + + expect(result).not.to be_nil + end end with "#to_s" do