Skip to content

Don't leak Tempfile instances. #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 13, 2025
Merged

Don't leak Tempfile instances. #204

merged 1 commit into from
Jul 13, 2025

Conversation

ioquatix
Copy link
Member

@ioquatix ioquatix commented Jul 13, 2025

Tempfile uses a finalizer that calls unlink which can be offloaded, and can be cancelled, resulting in the following log:

				describe Async::HTTP::Protocol::HTTP10 it behaves like a protocol with multiple client requests
					describe Async::HTTP::Protocol::HTTP10 it behaves like a protocol with multiple client requests it doesn't cancel all requests test/async/http/protocol/http10.rb:10:575
/home/samuel/.gem/ruby/3.4.0/gems/console-1.31.0/lib/console/filter.rb:169: warning: Exception in finalizer #<Tempfile::FinalizerManager:0x00007f5354070d38 @open_files={}, @path="/tmp/20250713-47323-4bqv5i", @pid=47323, @unlinked=false>
/home/samuel/Developer/socketry/async/lib/async/scheduler.rb:244:in 'IO::Event::Selector::URing#transfer': Async::Stop (Async::Stop)
	from /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:244:in 'Async::Scheduler#block'
	from /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:59:in 'IO::Event::WorkerPool#call'
	from /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:59:in 'Async::Scheduler::BlockingOperationWait#blocking_operation_wait'
	from /home/samuel/.rubies/ruby-head/lib/ruby/3.5.0+2/tempfile.rb:392:in 'File.unlink'
	from /home/samuel/.rubies/ruby-head/lib/ruby/3.5.0+2/tempfile.rb:392:in 'Tempfile::FinalizerManager#call'
	from /home/samuel/.gem/ruby/3.4.0/gems/console-1.31.0/lib/console/filter.rb:169:in 'Kernel#is_a?'
	from /home/samuel/.gem/ruby/3.4.0/gems/console-1.31.0/lib/console/filter.rb:169:in 'Console::Filter#enabled?'
	from /home/samuel/.gem/ruby/3.4.0/gems/console-1.31.0/lib/console/filter.rb:47:in 'block (3 levels) in []'
	from /home/samuel/.gem/ruby/3.4.0/gems/console-1.31.0/lib/console/interface.rb:33:in 'Console::Interface#debug'
	from /home/samuel/.gem/ruby/3.4.0/gems/async-pool-0.10.3/lib/async/pool/controller.rb:272:in 'Async::Pool::Controller#reuse'
	from /home/samuel/.gem/ruby/3.4.0/gems/async-pool-0.10.3/lib/async/pool/controller.rb:154:in 'Async::Pool::Controller#release'
	from /home/samuel/Developer/socketry/async/external/async-http/lib/async/http/protocol/http1/client.rb:29:in 'Async::HTTP::Protocol::HTTP1::Client#closed'
	from /home/samuel/.gem/ruby/3.4.0/gems/protocol-http1-0.34.0/lib/protocol/http1/connection.rb:725:in 'Protocol::HTTP1::Connection#close!'
	from /home/samuel/.gem/ruby/3.4.0/gems/protocol-http1-0.34.0/lib/protocol/http1/connection.rb:776:in 'Protocol::HTTP1::Connection#receive_end_stream!'
	from /home/samuel/.gem/ruby/3.4.0/gems/protocol-http1-0.34.0/lib/protocol/http1/body/fixed.rb:63:in 'Protocol::HTTP1::Body::Fixed#read'
	from /home/samuel/.gem/ruby/3.4.0/gems/protocol-http-0.51.0/lib/protocol/http/body/readable.rb:96:in 'Protocol::HTTP::Body::Readable#each'
	from /home/samuel/.gem/ruby/3.4.0/gems/protocol-http-0.51.0/lib/protocol/http/body/buffered.rb:40:in 'Protocol::HTTP::Body::Buffered.read'
	from /home/samuel/.gem/ruby/3.4.0/gems/protocol-http-0.51.0/lib/protocol/http/body/readable.rb:157:in 'Protocol::HTTP::Body::Readable#finish'
	from /home/samuel/.gem/ruby/3.4.0/gems/protocol-http-0.51.0/lib/protocol/http/body/reader.rb:41:in 'Protocol::HTTP::Body::Reader#finish'
	from /home/samuel/Developer/socketry/async/external/async-http/fixtures/async/http/a_protocol.rb:586:in 'block (6 levels) in <module:HTTP>'
	from /home/samuel/Developer/socketry/async/external/async-http/fixtures/async/http/a_protocol.rb:584:in 'Kernel#loop'
	from /home/samuel/Developer/socketry/async/external/async-http/fixtures/async/http/a_protocol.rb:584:in 'block (5 levels) in <module:HTTP>'
	from /home/samuel/Developer/socketry/async/lib/async/task.rb:205:in 'block in Async::Task#run'
	from /home/samuel/Developer/socketry/async/lib/async/task.rb:443:in 'block in Async::Task#schedule'
    1m     warn: Async::HTTP::Client: running example [oid=0x3e28] [ec=0x3e30] [pid=47323] [2025-07-13 23:16:04 +1200]
               | Waiting for Async::HTTP::Protocol::HTTP10 pool to drain: #<Async::Pool::Controller(20/∞) 1/1*/84663;1/1*/84662;0/1*/15;1/1*/84661;1/1*/84662;1/1*/84661;1/1*/84661;1/1*/84660;1/1*/84661;1/1*/84660;1/1*/84660;0/1*/26;1/1*/84536;1/1*/84659;1/1*/84659;1/1*/84659;0/1*/32;1/1*/84658;1/1*/84658;1/1*/84657>

After this, the cancellation is ignored, and the test hangs/fails.

For now, let's use Tempfile in a way that doesn't cause finalizers to run.

Types of Changes

  • Maintenance.

Contribution

@ioquatix ioquatix merged commit 84502fe into main Jul 13, 2025
33 of 43 checks passed
@ioquatix ioquatix deleted the tempfile-close branch July 13, 2025 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant