Skip to content

Commit ab5fc4d

Browse files
authored
Merge pull request jruby#8478 from headius/clear_interrupts_when_interrupting
Avoid re-polling while reporting a Thread#raise
2 parents 185eb9d + 39cd7eb commit ab5fc4d

File tree

4 files changed

+94
-13
lines changed

4 files changed

+94
-13
lines changed

core/src/main/java/org/jruby/Ruby.java

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,15 @@
172172
import java.io.InputStream;
173173
import java.io.PrintStream;
174174
import java.io.PrintWriter;
175+
import java.io.Writer;
175176
import java.lang.invoke.MethodHandle;
176177
import java.lang.ref.WeakReference;
177178
import java.net.BindException;
179+
import java.nio.ByteBuffer;
180+
import java.nio.channels.Channels;
181+
import java.nio.channels.WritableByteChannel;
178182
import java.nio.charset.Charset;
183+
import java.nio.charset.StandardCharsets;
179184
import java.nio.charset.UnsupportedCharsetException;
180185
import java.security.SecureRandom;
181186
import java.util.ArrayList;
@@ -2848,6 +2853,25 @@ WarnCallback getRegexpWarnings() {
28482853
return regexpWarnings;
28492854
}
28502855

2856+
public IRubyObject getStderr() {
2857+
return getGlobalVariables().get("$stderr");
2858+
}
2859+
2860+
/**
2861+
* Return the original stderr with which this runtime was initialized.
2862+
*
2863+
* Used for fast-path comparisons when printing error info directly to stderr.
2864+
*
2865+
* @return the original stderr with which this runtime was initialized
2866+
*/
2867+
public IRubyObject getOriginalStderr() {
2868+
return originalStderr;
2869+
}
2870+
2871+
void setOriginalStderr(IRubyObject stderr) {
2872+
this.originalStderr = stderr;
2873+
}
2874+
28512875
public PrintStream getErrorStream() {
28522876
// FIXME: We can't guarantee this will always be a RubyIO...so the old code here is not safe
28532877
/*java.io.OutputStream os = ((RubyIO) getGlobalVariables().getService("$stderr")).getOutStream();
@@ -2916,36 +2940,41 @@ private static boolean isJavaPackageOrJavaClassProxyType(final RubyModule type)
29162940
return type instanceof JavaPackage || ClassUtils.isJavaClassProxyType(type);
29172941
}
29182942

2919-
/** Prints an error with backtrace to the error stream.
2943+
/**
2944+
* Prints a Ruby exception with backtrace to the configured stderr stream.
29202945
*
29212946
* MRI: eval.c - error_print()
29222947
*
29232948
*/
29242949
public void printError(final RubyException ex) {
29252950
if (ex == null) return;
29262951

2927-
PrintStream errorStream = getErrorStream();
2928-
String backtrace = config.getTraceType().printBacktrace(ex, (errorStream == System.err) && getPosix().isatty(FileDescriptor.err));
2929-
try {
2930-
errorStream.print(backtrace);
2931-
} catch (Exception e) {
2932-
System.err.print(backtrace);
2933-
}
2952+
boolean formatted =
2953+
getStderr() == getOriginalStderr() &&
2954+
getErr() == System.err &&
2955+
getPosix().isatty(FileDescriptor.err);
2956+
2957+
String backtrace = config.getTraceType().printBacktrace(ex, formatted);
2958+
printErrorString(backtrace);
29342959
}
29352960

2961+
/**
2962+
* Prints an exception to System.err.
2963+
*
2964+
* @param ex
2965+
*/
29362966
public void printError(final Throwable ex) {
29372967
if (ex instanceof RaiseException) {
29382968
printError(((RaiseException) ex).getException());
29392969
return;
29402970
}
29412971

29422972
ByteArrayOutputStream baos = new ByteArrayOutputStream();
2943-
PrintStream errorStream = getErrorStream();
29442973

29452974
ex.printStackTrace(new PrintStream(baos));
29462975

29472976
try {
2948-
errorStream.write(baos.toByteArray());
2977+
printErrorString(baos.toByteArray());
29492978
} catch (Exception e) {
29502979
try {
29512980
System.err.write(baos.toByteArray());
@@ -2956,6 +2985,50 @@ public void printError(final Throwable ex) {
29562985
}
29572986
}
29582987

2988+
/**
2989+
* Prints a string directly to the stderr channel, if default, or via dynamic dispatch otherwise.
2990+
*
2991+
* @param msg the string to print
2992+
*/
2993+
public void printErrorString(String msg) {
2994+
IRubyObject stderr = getStderr();
2995+
2996+
WritableByteChannel writeChannel;
2997+
if (stderr == getOriginalStderr() &&
2998+
(writeChannel = ((RubyIO) stderr).getOpenFile().fd().chWrite) != null) {
2999+
Writer writer = Channels.newWriter(writeChannel, "UTF-8");
3000+
try {
3001+
writer.write(msg);
3002+
writer.flush();
3003+
} catch (IOException ioe) {
3004+
// ignore as in CRuby
3005+
}
3006+
} else {
3007+
getErrorStream().print(msg);
3008+
}
3009+
}
3010+
3011+
/**
3012+
* Prints a string directly to the stderr channel, if default, or via dynamic dispatch otherwise.
3013+
*
3014+
* @param msg the string to print
3015+
*/
3016+
public void printErrorString(byte[] msg) {
3017+
IRubyObject stderr = getGlobalVariables().get("$stderr");
3018+
3019+
try {
3020+
WritableByteChannel writeChannel;
3021+
if (stderr == getOriginalStderr() &&
3022+
(writeChannel = ((RubyIO) stderr).getOpenFile().fd().chWrite) != null) {
3023+
writeChannel.write(ByteBuffer.wrap(msg));
3024+
} else {
3025+
getErrorStream().write(msg);
3026+
}
3027+
} catch (IOException ioe) {
3028+
// ignore as in CRuby
3029+
}
3030+
}
3031+
29593032
static final String ROOT_FRAME_NAME = "(root)";
29603033
static long yarpTime = 0;
29613034
static boolean loaded = false;
@@ -5724,6 +5797,8 @@ public void warn(String message) {
57245797
private final EnumMap<DefinedMessage, RubyString> definedMessages = new EnumMap<>(DefinedMessage.class);
57255798
private final EnumMap<RubyThread.Status, RubyString> threadStatuses = new EnumMap<>(RubyThread.Status.class);
57265799

5800+
private IRubyObject originalStderr;
5801+
57275802
public interface ObjectSpacer {
57285803
void addToObjectSpace(Ruby runtime, boolean useObjectSpace, IRubyObject object);
57295804
}

core/src/main/java/org/jruby/RubyGlobal.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ public static void initSTDIO(Ruby runtime, GlobalVariables globals) {
311311
runtime.defineGlobalConstant("STDIN", stdin);
312312
runtime.defineGlobalConstant("STDOUT", stdout);
313313
runtime.defineGlobalConstant("STDERR", stderr);
314+
315+
runtime.setOriginalStderr(stderr);
314316
} else {
315317
((RubyIO) runtime.getObject().getConstant("STDIN")).getOpenFile().setFD(stdin.getOpenFile().fd());
316318
((RubyIO) runtime.getObject().getConstant("STDOUT")).getOpenFile().setFD(stdout.getOpenFile().fd());

core/src/main/java/org/jruby/RubyThread.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,17 @@
3434
package org.jruby;
3535

3636
import java.io.IOException;
37+
import java.io.Writer;
3738
import java.lang.management.ManagementFactory;
3839
import java.lang.ref.WeakReference;
3940
import java.nio.ByteBuffer;
4041
import java.nio.channels.Channel;
42+
import java.nio.channels.Channels;
4143
import java.nio.channels.SelectableChannel;
4244
import java.nio.channels.SelectionKey;
4345
import java.nio.channels.Selector;
46+
import java.nio.channels.WritableByteChannel;
47+
import java.nio.charset.StandardCharsets;
4448
import java.util.Iterator;
4549
import java.util.Objects;
4650
import java.util.Queue;
@@ -275,7 +279,6 @@ private void executeInterrupts(ThreadContext context, boolean blockingTiming) {
275279
((RubyFixnum) err).getLongValue() == 2)) {
276280
toKill();
277281
} else {
278-
afterBlockingCall();
279282
if (getStatus() == Status.SLEEP) {
280283
exitSleep();
281284
}
@@ -2071,7 +2074,9 @@ public void exceptionRaised(RaiseException exception) {
20712074
protected void printReportExceptionWarning() {
20722075
Ruby runtime = getRuntime();
20732076
String name = threadImpl.getReportName();
2074-
runtime.getErrorStream().println("warning: thread \"" + name + "\" terminated with exception (report_on_exception is true):");
2077+
String warning = "warning: thread \"" + name + "\" terminated with exception (report_on_exception is true):";
2078+
2079+
runtime.printErrorString(warning);
20752080
}
20762081

20772082
/**
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
fails:Thread#report_on_exception= when set to true prints a backtrace on $stderr in the regular backtrace order
2-
fails:Thread#report_on_exception= when set to true prints the backtrace even if the thread was killed just after Thread#raise

0 commit comments

Comments
 (0)