Skip to content
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

Calling File.each_line Iterator method doesn't close File #6087

Closed
bmulvihill opened this issue May 10, 2018 · 15 comments · Fixed by #6301
Closed

Calling File.each_line Iterator method doesn't close File #6087

bmulvihill opened this issue May 10, 2018 · 15 comments · Fixed by #6301

Comments

@bmulvihill
Copy link
Contributor

While working on something I discovered this bug: crystal-lang-tools/scry#116, where we were getting a Too many open files error. This is because we were using the iterator version of File.each_line method.

crystal/src/file.cr

Lines 627 to 629 in 0e64622

def self.each_line(filename, encoding = nil, invalid = nil, chomp = true)
open(filename, "r", encoding: encoding, invalid: invalid).each_line(chomp: chomp)
end

It appears this method does not close the file, nor does there appear to be an obvious way to close a file that is opened via this way since an Iterator is returned.

@RX14
Copy link
Contributor

RX14 commented May 10, 2018

Ah yeah, this is the same as I hit in #5623.

@RX14
Copy link
Contributor

RX14 commented May 10, 2018

I vote for remove these methods - make people tie them into a specific File instance which is (hopefully) tied to a File.open block which mitigates the problem.

@Sija
Copy link
Contributor

Sija commented May 10, 2018

@RX14 makes sense for the iterator version, but why the yielding one, since it's useful and doesn't leak? EDIT: my bad, nvm.

@RX14
Copy link
Contributor

RX14 commented May 10, 2018

@Sija i never said to remove the yielding one...

@asterite
Copy link
Member

#780

@RX14
Copy link
Contributor

RX14 commented May 10, 2018

@asterite relevant but I don't think it interesects here. The problem with File.each_line is that you're creating a File instance that always leaks and you cannot close. Hence we must remove the method.

@asterite
Copy link
Member

Yes, you can close it. The GC will close it once you are done with it. That's the whole point of a GC. And why I linked the issue: once that file isn't used, but the GC didn't run, and you try to create a new file and you run out of descriptors, run the GC to close those files.

Ruby works fine and has exactly the same method.

@RX14
Copy link
Contributor

RX14 commented May 10, 2018

@asterite I consider the GC closing FDs as a crutch, and you can't manually close the iterator you have to wait for the GC.

Maybe I should reconsider... and maybe we should just run the GC before we report EMFILE and copy ruby. But i don't like it.

@straight-shoota
Copy link
Member

The GC won't clean up and close the file handle if it's still referenced somewhere. Shouldn't rely on that.

@yxhuvud
Copy link
Contributor

yxhuvud commented May 20, 2018

Could this be fixed by adding a flag to File#each_line to tell it to close itself when it reach the end?

@jhass
Copy link
Member

jhass commented May 20, 2018

Why don't we just provide a close method on file related iterators and return an iterator that calls that for class methods when at the end? Probably could even be a generic one. I think it's rather unlikely that you don't consume those to the end or even want to auto close it if you do do not.

@RX14
Copy link
Contributor

RX14 commented May 20, 2018

@jhass but I think the difference between

File.each_line(foo).map { ... }

and

File.open(foo) do |file|
  file.each_line.map { ... }
end

isn't much of a deal to type and the latter is so much clearer about the scope. So I'd prefer the latter instead of fixing the former.

@jhass
Copy link
Member

jhass commented May 21, 2018

I like having these available as single line or chain. That would become almost impossible then without heavy &. usage.

@j8r
Copy link
Contributor

j8r commented May 21, 2018

File.each_line(foo)

vs

File.open(foo) &.each_line

They are fairly close. I also think that File.each_line can be removed.
For what it adds, it doesn't worth the hassle.

@eliasjpr
Copy link

each_line should be in an instance of a file

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 a pull request may close this issue.

9 participants