Skip to content

Commit

Permalink
Fix gets on Windows. Fixes #2959
Browse files Browse the repository at this point in the history
* Don't unwrap stdin streams; the direct FileChannel does not seem
  to be usable and reads result in IOException: Not enough storage
  is available to process this command.
* Tweaks for argf logic.
* Always treat FileChannel as ready for read, since files always
  select immediately and processes can't be selected without
  native support.
headius committed May 28, 2015
1 parent f37dc99 commit 3a5dda7
Showing 4 changed files with 21 additions and 35 deletions.
17 changes: 4 additions & 13 deletions core/src/main/java/org/jruby/RubyArgsFile.java
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
import java.io.IOException;
import static org.jruby.RubyEnumerator.enumeratorize;

import org.jruby.anno.FrameField;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
import jnr.posix.FileStat;
@@ -239,6 +240,7 @@ public static IRubyObject external_encoding(ThreadContext context, IRubyObject r
return ((RubyIO) getData(context, recv, "no stream to set encoding").currentFile).external_encoding(context);
}

// MRI: argf_getline
private static IRubyObject argf_getline(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
ArgsFileData data = ArgsFileData.getDataFrom(recv);

@@ -269,21 +271,10 @@ private static IRubyObject argf_getline(ThreadContext context, IRubyObject recv,
/** Read a line.
*
*/
@JRubyMethod(name = "gets", optional = 1)
@JRubyMethod(name = "gets", optional = 1, writes = FrameField.LASTLINE)
public static IRubyObject gets(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
ArgsFileData data = ArgsFileData.getDataFrom(recv);

if (!data.next_argv(context)) return context.runtime.getNil();

IRubyObject line;
if (!(data.currentFile instanceof RubyIO)) {
line = data.currentFile.callMethod(context, "gets", args);
} else {
line = argf_getline(context, recv, args);
}

IRubyObject line = argf_getline(context, recv, args);
context.setLastLine(line);
context.runtime.getGlobalVariables().set("$_", line);

return line;
}
10 changes: 0 additions & 10 deletions core/src/main/java/org/jruby/RubyGlobal.java
Original file line number Diff line number Diff line change
@@ -286,19 +286,9 @@ private static Channel prepareStdioChannel(Ruby runtime, STDIO stdio, Object str
} else {
switch (stdio) {
case IN:
stream = ShellLauncher.unwrapFilterInputStream((InputStream)stream);
if (stream instanceof FileInputStream) {
return ((FileInputStream)stream).getChannel();
}

return Channels.newChannel((InputStream)stream);
case OUT:
case ERR:
stream = ShellLauncher.unwrapFilterOutputStream((OutputStream)stream);
if (stream instanceof FileOutputStream) {
return ((FileOutputStream)stream).getChannel();
}

return Channels.newChannel((OutputStream)stream);
default: throw new RuntimeException("invalid stdio: " + stdio);
}
8 changes: 7 additions & 1 deletion core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
@@ -318,9 +318,15 @@ public static IRubyObject getc(ThreadContext context, IRubyObject recv) {
return defin.callMethod(context, "getc");
}

// MRI: rb_f_gets
@JRubyMethod(optional = 1, module = true, visibility = PRIVATE)
public static IRubyObject gets(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
return RubyArgsFile.gets(context, context.runtime.getArgsFile(), args);
Ruby runtime = context.runtime;

if (recv == runtime.getArgsFile()) {
return RubyArgsFile.gets(context, runtime.getArgsFile(), args);
}
return runtime.getArgsFile().callMethod(context, "gets", args);
}

@JRubyMethod(optional = 1, module = true, visibility = PRIVATE)
21 changes: 10 additions & 11 deletions core/src/main/java/org/jruby/util/io/OpenFile.java
Original file line number Diff line number Diff line change
@@ -1398,18 +1398,17 @@ boolean waitReadable(ThreadContext context, ChannelFD fd) {
}
}

// kinda-hacky way to see if there's more data to read from a seekable channel
/*
Seekable channels (usually FileChannel) are treated as ready always. There are
three kinds we typically see:
1. files, which always select(2) as ready
2. stdio, which we can't select and can't check .size for available data
3. subprocess stdio, which we can't select and can't check .size either
In all three cases, without native fd logic, we can't do anything to determine
if the stream is ready, so we just assume it is and hope for the best.
*/
if (fd.chSeek != null) {
FileChannel fdSeek = fd.chSeek;
try {
// not a real file, can't get size...we'll have to just read and block
if (fdSeek.size() < 0) return true;

// if current position is less than file size, read should not block
return fdSeek.position() < fdSeek.size();
} catch (IOException ioe) {
throw context.runtime.newIOErrorFromException(ioe);
}
return true;
}
} finally {
if (locked) unlock();

0 comments on commit 3a5dda7

Please sign in to comment.