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 1 year ago
What do you expect for the dup
ped Tempfile
object?
An IO
to the same file?
Or another temporary file?
Updated by stac47 (Laurent Stacul) over 1 year 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 1 year 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 1 year ago
- Subject changed from Closing an IO#dup behaviour to Closing an Tempfile#dup behaviour
Updated by stac47 (Laurent Stacul) over 1 year 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 dup
ed 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 1 year 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) 10 months 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]