Skip to content

Commit ee351ad

Browse files
committedNov 19, 2017
Merge branch 'jruby-9.1'
2 parents a41e22b + efbf2af commit ee351ad

File tree

3 files changed

+97
-27
lines changed

3 files changed

+97
-27
lines changed
 

Diff for: ‎core/src/main/java/org/jruby/RubyIO.java

+87-18
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import jnr.enxio.channels.NativeSelectableChannel;
4242
import jnr.posix.POSIX;
4343
import org.jcodings.transcode.EConvFlags;
44+
import org.jruby.api.API;
4445
import org.jruby.runtime.Helpers;
4546
import org.jruby.runtime.JavaSites.IOSites;
4647
import org.jruby.runtime.callsite.RespondToCallSite;
@@ -2185,16 +2186,79 @@ public IRubyObject close_read(ThreadContext context) {
21852186
return rbIoClose(context);
21862187
}
21872188

2188-
@JRubyMethod(name = "close_on_exec=", notImplemented = true)
2189+
public static final int FD_CLOEXEC = 1;
2190+
2191+
@JRubyMethod(name = "close_on_exec=")
21892192
public IRubyObject close_on_exec_set(ThreadContext context, IRubyObject arg) {
2190-
// TODO: rb_io_set_close_on_exec
2191-
throw context.runtime.newNotImplementedError("close_on_exec=");
2193+
Ruby runtime = context.runtime;
2194+
POSIX posix = runtime.getPosix();
2195+
OpenFile fptr = getOpenFileChecked();
2196+
RubyIO write_io;
2197+
2198+
if (fptr.fd().chNative == null || !posix.isNative()) {
2199+
runtime.getWarnings().warning("close_on_exec is not implemented for this stream type: " + fptr.fd().ch.getClass().getSimpleName());
2200+
return context.nil;
2201+
}
2202+
2203+
int flag = arg.isTrue() ? FD_CLOEXEC : 0;
2204+
int fd, ret;
2205+
2206+
write_io = GetWriteIO();
2207+
if (this != write_io) {
2208+
fptr = write_io.getOpenFileChecked();
2209+
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
2210+
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) return API.rb_sys_fail_path(runtime, fptr.getPath());
2211+
if ((ret & FD_CLOEXEC) != flag) {
2212+
ret = (ret & ~FD_CLOEXEC) | flag;
2213+
ret = posix.fcntlInt(fd, Fcntl.F_SETFD, ret);
2214+
if (ret == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
2215+
}
2216+
}
2217+
2218+
}
2219+
2220+
fptr = getOpenFileChecked();
2221+
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
2222+
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
2223+
if ((ret & FD_CLOEXEC) != flag) {
2224+
ret = (ret & ~FD_CLOEXEC) | flag;
2225+
ret = posix.fcntlInt(fd, Fcntl.F_SETFD, ret);
2226+
if (ret == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
2227+
}
2228+
}
2229+
2230+
return context.nil;
21922231
}
21932232

2194-
@JRubyMethod(name = "close_on_exec?", notImplemented = true)
2233+
@JRubyMethod(name = {"close_on_exec?", "close_on_exec"})
21952234
public IRubyObject close_on_exec_p(ThreadContext context) {
2196-
// TODO: rb_io_close_on_exec_p
2197-
throw context.runtime.newNotImplementedError("close_on_exec=");
2235+
Ruby runtime = context.runtime;
2236+
POSIX posix = runtime.getPosix();
2237+
OpenFile fptr = getOpenFileChecked();
2238+
2239+
if (fptr == null || fptr.fd().chNative == null
2240+
|| !posix.isNative()) {
2241+
return context.fals;
2242+
}
2243+
2244+
RubyIO write_io;
2245+
int fd, ret;
2246+
2247+
write_io = GetWriteIO();
2248+
if (this != write_io) {
2249+
fptr = write_io.getOpenFileChecked();
2250+
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
2251+
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
2252+
if ((ret & FD_CLOEXEC) == 0) return context.fals;
2253+
}
2254+
}
2255+
2256+
fptr = getOpenFileChecked();
2257+
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
2258+
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
2259+
if ((ret & FD_CLOEXEC) == 0) return context.fals;
2260+
}
2261+
return context.tru;
21982262
}
21992263

22002264
/** Flushes the IO output stream.
@@ -2334,15 +2398,11 @@ public void setBlocking(boolean blocking) {
23342398

23352399
@JRubyMethod(name = "fcntl")
23362400
public IRubyObject fcntl(ThreadContext context, IRubyObject cmd) {
2337-
// TODO: This version differs from ioctl by checking whether fcntl exists
2338-
// and raising notimplemented if it doesn't; perhaps no difference for us?
23392401
return ctl(context.runtime, cmd, null);
23402402
}
23412403

23422404
@JRubyMethod(name = "fcntl")
23432405
public IRubyObject fcntl(ThreadContext context, IRubyObject cmd, IRubyObject arg) {
2344-
// TODO: This version differs from ioctl by checking whether fcntl exists
2345-
// and raising notimplemented if it doesn't; perhaps no difference for us?
23462406
return ctl(context.runtime, cmd, arg);
23472407
}
23482408

@@ -2360,7 +2420,7 @@ public IRubyObject ioctl(ThreadContext context, IRubyObject[] args) {
23602420
return ctl(context.runtime, cmd, arg);
23612421
}
23622422

2363-
public IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {
2423+
private IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {
23642424
long realCmd = cmd.convertToInteger().getLongValue();
23652425
long nArg = 0;
23662426

@@ -2383,23 +2443,32 @@ public IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {
23832443

23842444
OpenFile fptr = getOpenFileChecked();
23852445

2386-
// Fixme: Only F_SETFL and F_GETFL is current supported
2387-
// FIXME: Only NONBLOCK flag is supported
2446+
// This currently only supports setting two flags:
2447+
// FD_CLOEXEC on platforms where it is supported, and
2448+
// O_NONBLOCK when the stream can be set to non-blocking.
2449+
23882450
// FIXME: F_SETFL and F_SETFD are treated as the same thing here. For the case of dup(fd) we
23892451
// should actually have F_SETFL only affect one (it is unclear how well we do, but this TODO
23902452
// is here to at least document that we might need to do more work here. Mostly SETFL is
23912453
// for mode changes which should persist across fork() boundaries. Since JVM has no fork
23922454
// this is not a problem for us.
23932455
if (realCmd == FcntlLibrary.FD_CLOEXEC) {
2394-
// Do nothing. FD_CLOEXEC has no meaning in JVM since we cannot really exec.
2395-
// And why the hell does webrick pass this in as a first argument!!!!!
2396-
} else if (realCmd == Fcntl.F_SETFL.intValue() || realCmd == Fcntl.F_SETFD.intValue()) {
2397-
if ((nArg & FcntlLibrary.FD_CLOEXEC) == FcntlLibrary.FD_CLOEXEC) {
2398-
// Do nothing. FD_CLOEXEC has no meaning in JVM since we cannot really exec.
2456+
close_on_exec_set(runtime.getCurrentContext(), runtime.getTrue());
2457+
} else if (realCmd == Fcntl.F_SETFD.intValue()) {
2458+
if (arg != null && (nArg & FcntlLibrary.FD_CLOEXEC) == FcntlLibrary.FD_CLOEXEC) {
2459+
close_on_exec_set(runtime.getCurrentContext(), arg);
23992460
} else {
2461+
throw runtime.newNotImplementedError("F_SETFD only supports FD_CLOEXEC");
2462+
}
2463+
} else if (realCmd == Fcntl.F_GETFD.intValue()) {
2464+
return runtime.newFixnum(close_on_exec_p(runtime.getCurrentContext()).isTrue() ? FD_CLOEXEC : 0);
2465+
} else if (realCmd == Fcntl.F_SETFL.intValue()) {
2466+
if ((nArg & OpenFlags.O_NONBLOCK.intValue()) != 0) {
24002467
boolean block = (nArg & ModeFlags.NONBLOCK) != ModeFlags.NONBLOCK;
24012468

24022469
fptr.setBlocking(runtime, block);
2470+
} else {
2471+
throw runtime.newNotImplementedError("F_SETFL only supports O_NONBLOCK");
24032472
}
24042473
} else if (realCmd == Fcntl.F_GETFL.intValue()) {
24052474
return fptr.isBlocking() ? RubyFixnum.zero(runtime) : RubyFixnum.newFixnum(runtime, ModeFlags.NONBLOCK);

Diff for: ‎core/src/main/java/org/jruby/api/API.java

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.jruby.api;
2+
3+
import org.jruby.Ruby;
4+
import org.jruby.runtime.builtin.IRubyObject;
5+
6+
public class API {
7+
public static IRubyObject rb_sys_fail_path(Ruby runtime, String path) {
8+
throw runtime.newSystemCallError("bad path for cloexec: " + path);
9+
}
10+
}

Diff for: ‎spec/tags/ruby/core/io/close_on_exec_tags.txt

-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1 @@
1-
fails:IO#close_on_exec= sets the close-on-exec flag if true
2-
fails:IO#close_on_exec= sets the close-on-exec flag if non-false
3-
fails:IO#close_on_exec= unsets the close-on-exec flag if false
4-
fails:IO#close_on_exec= unsets the close-on-exec flag if nil
51
fails:IO#close_on_exec= ensures the IO's file descriptor is closed in exec'ed processes
6-
fails:IO#close_on_exec= raises IOError if called on a closed IO
7-
fails:IO#close_on_exec= returns nil
8-
fails:IO#close_on_exec? returns true if set
9-
fails:IO#close_on_exec? raises IOError if called on a closed IO
10-
fails:IO#close_on_exec? returns true by default

0 commit comments

Comments
 (0)
Please sign in to comment.