Bug #19441
closedClosing an Tempfile#dup behaviour
Description
Hello amazing ruby folks!
I classified that ticket as a "Bug" although it is perhaps something I did not understand.
Here is a ruby session:
irb(main):001:0> file = Tempfile.new
=> #<File:/var/folders/m2/bljzrgq160vbf0vk466k_7gw0000gn/T/20230216-39664-davfmj>
irb(main):002:0> file.fileno
=> 9
irb(main):003:0> dup = file.dup
=> #<File:/var/folders/m2/bljzrgq160vbf0vk466k_7gw0000gn/T/20230216-39664-davfmj>
irb(main):004:0> dup.fileno
=> 10
irb(main):005:0> dup.close
=> nil
irb(main):006:0> dup.closed?
=> false
irb(main):007:0> file.closed?
=> true
The two last lines are unexpected to me. I would have expected the converse:
irb(main):006:0> dup.closed?
=> true
irb(main):007:0> file.closed?
=> false
I tried this scenario in latest ruby but also in ruby 2.7.
Thanks in advance for helping me to understand this behaviour.
Updated by nobu (Nobuyoshi Nakada) over 2 years ago
What do you expect for the dupped Tempfile object?
An IO to the same file?
Or another temporary file?
Updated by stac47 (Laurent Stacul) over 2 years ago
Provided #dup generally returns an object of the same type, I would expect to have a Tempfile object.
The unexpected thing was that if I replay the same scenario with File, I have what I expect. I was just wondering whether the behaviour discrepancy between File and Tempfile was normal.
irb(main):001:0> file = File.new("a_file.txt", "w+")
=> #<File:a_file.txt>
irb(main):002:0> file.size
=> 0
irb(main):003:0> dup = file.dup
=> #<File:a_file.txt>
irb(main):004:0> dup.closed?
=> false
irb(main):005:0> file.closed?
=> false
irb(main):006:0> dup.close
=> nil
irb(main):007:0> dup.closed?
=> true
irb(main):008:0> file.closed?
=> false
Updated by nobu (Nobuyoshi Nakada) over 2 years ago
A Tempfile uses a finalizer to clean up the target file.
That is the file will be removed when a dup of Tempfile is discarded (and also will be tried to remove again).
I don't think it is a good idea to use Tempfile#dup.
What do you want to achieve by using this method?
Updated by byroot (Jean Boussier) over 2 years ago
- Subject changed from Closing an IO#dup behaviour to Closing an Tempfile#dup behaviour
Updated by stac47 (Laurent Stacul) over 2 years ago
Thanks nobu for your answer it makes perfect sense.
My case which led to the current question comes from a Rails application which has a controller receiving a file. My input params[:file] hold an ActionDispatch::Http::UploadedFile which itself has an attribute tempfile.
In the controller, params[:file] is passed to some functions (I don't have the control of) that can read and close the underlying tempfile. As a matter a fact, the code calls ActionDispatch::Http::UploadedFile#open (shortcut to tempfile#open) after the calls that close the file.
I was wondering whether I could avoid calling #open by providing the methods which can close the file with a duped file descriptor so that the original ActionDispatch::Http::UploadedFile is left untouched until I really use it in my own code.
The scenario in itself is not really interesting, it is just I was surprised by the behaviour when I used Tempfile#dup but your explanation is acceptable. Thanks for this.
Updated by jeremyevans0 (Jeremy Evans) over 2 years ago
I think this is a bug in Tempfile#{dup,clone} that we should fix. I submitted a pull request for it: https://github.com/ruby/tempfile/pull/17
Updated by jeremyevans (Jeremy Evans) almost 2 years ago
- Status changed from Open to Closed
Applied in changeset git|ddcfc9feabf22ed6cc1071e65948a1d512a906fe.
[ruby/tempfile] Fix Tempfile#{dup,clone}
Instead of storing the delegate in @tmpfile, use getobj, since
delegate library already handles dup/clone for that. Copy the
unlinked, mode, and opts instance variables to the returned object
when using dup/clone.
Split the close/unlink finalizer into two finalizers. The close
finalizer always closes when any Tempfile instance is GCed, since
each Tempfile instance uses a separate file descriptor. The unlink
finalizer unlinks only when the original and all duped/cloned
Tempfiles are GCed, since all share the same path.
For Tempfile#open, undefine the close finalizer after closing the
current file, the redefine the close finalizer with the new file.
Fixes [Bug #19441]