Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1bcf17174e00
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: efbf2af718a2
Choose a head ref
  • 2 commits
  • 3 files changed
  • 1 contributor

Commits on Nov 19, 2017

  1. Copy the full SHA
    5594fd4 View commit details
  2. Untag passing close_on_exec specs.

    Note that the main spec, which tests that the flag is actually set
    still does not pass because it uses a temporary file, and our
    implementation of Tempfile does not use a native channel.
    headius committed Nov 19, 2017
    Copy the full SHA
    efbf2af View commit details
Showing with 97 additions and 27 deletions.
  1. +87 −18 core/src/main/java/org/jruby/RubyIO.java
  2. +10 −0 core/src/main/java/org/jruby/api/API.java
  3. +0 −9 spec/tags/ruby/core/io/close_on_exec_tags.txt
105 changes: 87 additions & 18 deletions core/src/main/java/org/jruby/RubyIO.java
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@
import jnr.enxio.channels.NativeSelectableChannel;
import jnr.posix.POSIX;
import org.jcodings.transcode.EConvFlags;
import org.jruby.api.API;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.JavaSites.IOSites;
import org.jruby.runtime.callsite.RespondToCallSite;
@@ -2167,16 +2168,79 @@ public IRubyObject close_read(ThreadContext context) {
return rbIoClose(runtime);
}

@JRubyMethod(name = "close_on_exec=", notImplemented = true)
public static final int FD_CLOEXEC = 1;

@JRubyMethod(name = "close_on_exec=")
public IRubyObject close_on_exec_set(ThreadContext context, IRubyObject arg) {
// TODO: rb_io_set_close_on_exec
throw context.runtime.newNotImplementedError("close_on_exec=");
Ruby runtime = context.runtime;
POSIX posix = runtime.getPosix();
OpenFile fptr = getOpenFileChecked();
RubyIO write_io;

if (fptr.fd().chNative == null || !posix.isNative()) {
runtime.getWarnings().warning("close_on_exec is not implemented for this stream type: " + fptr.fd().ch.getClass().getSimpleName());
return context.nil;
}

int flag = arg.isTrue() ? FD_CLOEXEC : 0;
int fd, ret;

write_io = GetWriteIO();
if (this != write_io) {
fptr = write_io.getOpenFileChecked();
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) return API.rb_sys_fail_path(runtime, fptr.getPath());
if ((ret & FD_CLOEXEC) != flag) {
ret = (ret & ~FD_CLOEXEC) | flag;
ret = posix.fcntlInt(fd, Fcntl.F_SETFD, ret);
if (ret == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
}
}

}

fptr = getOpenFileChecked();
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
if ((ret & FD_CLOEXEC) != flag) {
ret = (ret & ~FD_CLOEXEC) | flag;
ret = posix.fcntlInt(fd, Fcntl.F_SETFD, ret);
if (ret == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
}
}

return context.nil;
}

@JRubyMethod(name = "close_on_exec?", notImplemented = true)
@JRubyMethod(name = {"close_on_exec?", "close_on_exec"})
public IRubyObject close_on_exec_p(ThreadContext context) {
// TODO: rb_io_close_on_exec_p
throw context.runtime.newNotImplementedError("close_on_exec=");
Ruby runtime = context.runtime;
POSIX posix = runtime.getPosix();
OpenFile fptr = getOpenFileChecked();

if (fptr == null || fptr.fd().chNative == null
|| !posix.isNative()) {
return context.fals;
}

RubyIO write_io;
int fd, ret;

write_io = GetWriteIO();
if (this != write_io) {
fptr = write_io.getOpenFileChecked();
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
if ((ret & FD_CLOEXEC) == 0) return context.fals;
}
}

fptr = getOpenFileChecked();
if (fptr != null && 0 <= (fd = fptr.fd().chNative.getFD())) {
if ((ret = posix.fcntl(fd, Fcntl.F_GETFD)) == -1) API.rb_sys_fail_path(runtime, fptr.getPath());
if ((ret & FD_CLOEXEC) == 0) return context.fals;
}
return context.tru;
}

/** Flushes the IO output stream.
@@ -2366,15 +2430,11 @@ public void setBlocking(boolean blocking) {

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

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

@@ -2392,7 +2452,7 @@ public IRubyObject ioctl(ThreadContext context, IRubyObject[] args) {
return ctl(context.runtime, cmd, arg);
}

public IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {
private IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {
long realCmd = cmd.convertToInteger().getLongValue();
long nArg = 0;

@@ -2415,23 +2475,32 @@ public IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {

OpenFile fptr = getOpenFileChecked();

// Fixme: Only F_SETFL and F_GETFL is current supported
// FIXME: Only NONBLOCK flag is supported
// This currently only supports setting two flags:
// FD_CLOEXEC on platforms where it is supported, and
// O_NONBLOCK when the stream can be set to non-blocking.

// FIXME: F_SETFL and F_SETFD are treated as the same thing here. For the case of dup(fd) we
// should actually have F_SETFL only affect one (it is unclear how well we do, but this TODO
// is here to at least document that we might need to do more work here. Mostly SETFL is
// for mode changes which should persist across fork() boundaries. Since JVM has no fork
// this is not a problem for us.
if (realCmd == FcntlLibrary.FD_CLOEXEC) {
// Do nothing. FD_CLOEXEC has no meaning in JVM since we cannot really exec.
// And why the hell does webrick pass this in as a first argument!!!!!
} else if (realCmd == Fcntl.F_SETFL.intValue() || realCmd == Fcntl.F_SETFD.intValue()) {
if ((nArg & FcntlLibrary.FD_CLOEXEC) == FcntlLibrary.FD_CLOEXEC) {
// Do nothing. FD_CLOEXEC has no meaning in JVM since we cannot really exec.
close_on_exec_set(runtime.getCurrentContext(), runtime.getTrue());
} else if (realCmd == Fcntl.F_SETFD.intValue()) {
if (arg != null && (nArg & FcntlLibrary.FD_CLOEXEC) == FcntlLibrary.FD_CLOEXEC) {
close_on_exec_set(runtime.getCurrentContext(), arg);
} else {
throw runtime.newNotImplementedError("F_SETFD only supports FD_CLOEXEC");
}
} else if (realCmd == Fcntl.F_GETFD.intValue()) {
return runtime.newFixnum(close_on_exec_p(runtime.getCurrentContext()).isTrue() ? FD_CLOEXEC : 0);
} else if (realCmd == Fcntl.F_SETFL.intValue()) {
if ((nArg & OpenFlags.O_NONBLOCK.intValue()) != 0) {
boolean block = (nArg & ModeFlags.NONBLOCK) != ModeFlags.NONBLOCK;

fptr.setBlocking(runtime, block);
} else {
throw runtime.newNotImplementedError("F_SETFL only supports O_NONBLOCK");
}
} else if (realCmd == Fcntl.F_GETFL.intValue()) {
return fptr.isBlocking() ? RubyFixnum.zero(runtime) : RubyFixnum.newFixnum(runtime, ModeFlags.NONBLOCK);
10 changes: 10 additions & 0 deletions core/src/main/java/org/jruby/api/API.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.jruby.api;

import org.jruby.Ruby;
import org.jruby.runtime.builtin.IRubyObject;

public class API {
public static IRubyObject rb_sys_fail_path(Ruby runtime, String path) {
throw runtime.newSystemCallError("bad path for cloexec: " + path);
}
}
9 changes: 0 additions & 9 deletions spec/tags/ruby/core/io/close_on_exec_tags.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1 @@
fails:IO#close_on_exec= sets the close-on-exec flag if true
fails:IO#close_on_exec= sets the close-on-exec flag if non-false
fails:IO#close_on_exec= unsets the close-on-exec flag if false
fails:IO#close_on_exec= unsets the close-on-exec flag if nil
fails:IO#close_on_exec= ensures the IO's file descriptor is closed in exec'ed processes
fails:IO#close_on_exec= raises IOError if called on a closed IO
fails:IO#close_on_exec= returns nil
fails:IO#close_on_exec? returns true if set
fails:IO#close_on_exec? raises IOError if called on a closed IO
fails:IO#close_on_exec? returns true by default