IO objects abandoned while still open will call close() in their finalizer. Taking advantage of this behavior, MRI guards every fd allocating syscall retrying the syscall after running the GC if EMFILE/ENFILE are encountered:
This is possible in MRI because rb_gc() blocks while the GC runs, guaranteeing that finalizers will have been invoked before the syscall is retried. System.gc() doesn't block, however, so this approach on the JVM will always fail.
The guards in jruby are also incomplete. As best I can tell they only exist around sysopen call, but don't exist around other descriptor allocating calls (directories, sockets, etc)
It seems to me that jruby could do one of several things:
I don't know of a good way to emulate the behavior of MRI in this case, unfortunately.
When I did the port of this and other similar code, I left this in mostly to keep the code consistent with MRI until I decided how to refactor it. I agree that this code is not effective in JRuby and it should be removed.
Your patch is mostly ok, but it leaves a nested if (fd == null) check in place. If you'd like to audit other places we call System.gc, they could probably benefit from similar treatment.
System.gc() is async, do not attempt to invoke it and retry sysopen a…
…s it will always fail
Fixed the nested (fd == null) check, thanks.
Jruby doesn't seem to have the other EMFILE guards around other descriptor generating calls like Dir or Socket. I do see a few invocations in places such as AllocatedNativeMemoryIO. I'm not entirely clear on whether that invocation is effective, but given the context it probably doesn't hurt since it tests and throws.
Merge branch 'master' of github.com:jruby/jruby
System.gc is still there (on 220.127.116.11) ... but the PR could use a rebase (or a hand merge)