Skip to content

Commit 6024a6c

Browse files
committed
[GR-18163] Fix handling of signals with --single-threaded (#2265)
PullRequest: truffleruby/2433
2 parents 9bd52da + 8157fdf commit 6024a6c

File tree

6 files changed

+57
-50
lines changed

6 files changed

+57
-50
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Bug fixes:
1313
* Fix `Thread.handle_interrupt` to defer non-pure interrupts until the end of the `handle_interrupt` block (#2219).
1414
* Clear and restore errinfo on entry and normal return from methods in C extensions (#2227).
1515
* Fix extra whitespace in squiggly heredoc with escaped newline (#2238, @wildmaples and @norswap).
16+
* Fix handling of signals with `--single-threaded` (#2265).
1617

1718
Compatibility:
1819

src/main/java/org/truffleruby/RubyContext.java

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
package org.truffleruby;
1111

1212
import java.io.File;
13+
import java.io.OutputStream;
14+
import java.io.PrintStream;
15+
import java.io.UnsupportedEncodingException;
1316
import java.nio.ByteBuffer;
1417
import java.security.NoSuchAlgorithmException;
1518
import java.security.SecureRandom;
@@ -96,6 +99,8 @@ public class RubyContext {
9699

97100
private final RubyLanguage language;
98101
@CompilationFinal private TruffleLanguage.Env env;
102+
@CompilationFinal private PrintStream outStream;
103+
@CompilationFinal private PrintStream errStream;
99104
@CompilationFinal private boolean hasOtherPublicLanguages;
100105

101106
@CompilationFinal private Options options;
@@ -154,15 +159,12 @@ public class RubyContext {
154159
public RubyContext(RubyLanguage language, TruffleLanguage.Env env) {
155160
Metrics.printTime("before-context-constructor");
156161

157-
this.preInitializing = env.isPreInitialization();
162+
this.language = language;
163+
setEnv(env);
158164
this.preInitialized = preInitializing;
159165

160166
preInitializationManager = preInitializing ? new PreInitializationManager() : null;
161167

162-
this.language = language;
163-
this.env = env;
164-
this.hasOtherPublicLanguages = computeHasOtherPublicLanguages(env);
165-
166168
options = createOptions(env, language.options);
167169

168170
warningCategoryDeprecated = new AssumedValue<>(options.WARN_DEPRECATED);
@@ -266,9 +268,7 @@ public void initialize() {
266268
* initialization which needs to be performed to adapt to the new process and external environment. Calls are kept
267269
* in the same order as during normal initialization. */
268270
protected boolean patch(Env newEnv) {
269-
this.env = newEnv;
270-
this.hasOtherPublicLanguages = computeHasOtherPublicLanguages(newEnv);
271-
this.preInitializing = newEnv.isPreInitialization();
271+
setEnv(newEnv);
272272
if (preInitializing) {
273273
throw CompilerDirectives.shouldNotReachHere("Expected patch Env#isPreInitialization() to be false");
274274
}
@@ -389,6 +389,22 @@ private Options createOptions(TruffleLanguage.Env env, LanguageOptions languageO
389389
return options;
390390
}
391391

392+
private void setEnv(Env env) {
393+
this.env = env;
394+
this.outStream = printStreamFor(env.out());
395+
this.errStream = printStreamFor(env.err());
396+
this.hasOtherPublicLanguages = computeHasOtherPublicLanguages(env);
397+
this.preInitializing = env.isPreInitialization();
398+
}
399+
400+
private static PrintStream printStreamFor(OutputStream outputStream) {
401+
try {
402+
return new PrintStream(outputStream, true, "UTF-8");
403+
} catch (UnsupportedEncodingException e) {
404+
throw CompilerDirectives.shouldNotReachHere(e);
405+
}
406+
}
407+
392408
private static boolean computeHasOtherPublicLanguages(Env env) {
393409
for (String language : env.getPublicLanguages().keySet()) {
394410
if (!language.equals(TruffleRuby.LANGUAGE_ID)) {
@@ -832,6 +848,14 @@ public AssumedValue<Boolean> getWarningCategoryExperimental() {
832848
return warningCategoryExperimental;
833849
}
834850

851+
public PrintStream getEnvOutStream() {
852+
return outStream;
853+
}
854+
855+
public PrintStream getEnvErrStream() {
856+
return errStream;
857+
}
858+
835859
@TruffleBoundary
836860
public static String fileLine(SourceSection section) {
837861
if (section == null) {

src/main/java/org/truffleruby/core/VMPrimitiveNodes.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
*/
3838
package org.truffleruby.core;
3939

40-
import java.io.PrintStream;
4140
import java.util.Map.Entry;
4241

4342
import com.oracle.truffle.api.TruffleStackTrace;
@@ -67,7 +66,6 @@
6766
import org.truffleruby.language.RubyDynamicObject;
6867
import org.truffleruby.language.SafepointPredicate;
6968
import org.truffleruby.language.backtrace.Backtrace;
70-
import org.truffleruby.language.backtrace.BacktraceFormatter;
7169
import org.truffleruby.language.control.ExitException;
7270
import org.truffleruby.language.control.RaiseException;
7371
import org.truffleruby.language.control.ThrowException;
@@ -254,18 +252,17 @@ protected boolean restoreDefault(Object signalString, Object action,
254252
@Specialization(guards = "libSignalString.isRubyString(signalString)")
255253
protected boolean watchSignalProc(Object signalString, RubyProc action,
256254
@CachedLibrary(limit = "2") RubyStringLibrary libSignalString) {
257-
if (getContext().getThreadManager().getCurrentThread() != getContext().getThreadManager().getRootThread()) {
255+
final RubyContext context = getContext();
256+
257+
if (context.getThreadManager().getCurrentThread() != context.getThreadManager().getRootThread()) {
258258
// The proc will be executed on the main thread
259259
SharedObjects.writeBarrier(getLanguage(), action);
260260
}
261261

262-
final RubyContext context = getContext();
263-
264-
String signalName = libSignalString.getJavaString(signalString);
262+
final String signalName = libSignalString.getJavaString(signalString);
265263
return registerHandler(signalName, signal -> {
266264
if (context.getOptions().SINGLE_THREADED) {
267-
RubyLanguage.LOGGER.severe(
268-
"signal " + signal + " caught but can't create a thread to handle it so ignoring");
265+
warnRestoreAndRaise(context, signalName, signal, "create a thread");
269266
return;
270267
}
271268

@@ -278,17 +275,7 @@ protected boolean watchSignalProc(Object signalString, RubyProc action,
278275
try {
279276
prev = truffleContext.enter(this);
280277
} catch (IllegalStateException e) { // Multi threaded access denied from Truffle
281-
// Not in a context, so we cannot use TruffleLogger
282-
final PrintStream printStream = BacktraceFormatter.printStreamFor(context.getEnv().err());
283-
printStream.println(
284-
"[ruby] SEVERE: signal " + signal +
285-
" caught but can't attach a thread to handle it so restoring the default handler and re-raising the signal");
286-
Signals.restoreDefaultHandler(signalName);
287-
try {
288-
Signal.raise(signal);
289-
} catch (IllegalArgumentException illegalArgumentException) {
290-
illegalArgumentException.printStackTrace(printStream);
291-
}
278+
warnRestoreAndRaise(context, signalName, signal, "attach a thread");
292279
return;
293280
}
294281
try {
@@ -302,6 +289,19 @@ protected boolean watchSignalProc(Object signalString, RubyProc action,
302289
});
303290
}
304291

292+
private static void warnRestoreAndRaise(RubyContext context, String signalName, Signal signal, String failure) {
293+
// Not in a context, so we cannot use TruffleLogger
294+
context.getEnvErrStream().println(
295+
"[ruby] SEVERE: signal " + signal + " caught but can't " + failure +
296+
" to handle it so restoring the default handler and re-raising the signal");
297+
Signals.restoreDefaultHandler(signalName);
298+
try {
299+
Signal.raise(signal);
300+
} catch (IllegalArgumentException illegalArgumentException) {
301+
illegalArgumentException.printStackTrace(context.getEnvErrStream());
302+
}
303+
}
304+
305305
@TruffleBoundary
306306
private boolean restoreDefaultHandler(String signalName) {
307307
if (getContext().getOptions().EMBEDDED) {

src/main/java/org/truffleruby/core/exception/CoreExceptions.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import static org.truffleruby.core.array.ArrayHelpers.createArray;
1313

1414
import java.io.IOException;
15-
import java.io.PrintStream;
1615
import java.util.EnumSet;
1716

1817
import com.oracle.truffle.api.interop.InvalidBufferOffsetException;
@@ -99,8 +98,7 @@ public void showExceptionIfDebug(RubyClass rubyClass, Object message, Backtrace
9998
Object stderr = context.getCoreLibrary().getStderr();
10099
RubyContext.send(stderr, "write", outputString);
101100
} else {
102-
final PrintStream printStream = BacktraceFormatter.printStreamFor(context.getEnv().err());
103-
printStream.println(output);
101+
context.getEnvErrStream().println(output);
104102
}
105103
}
106104
}

src/main/java/org/truffleruby/core/kernel/KernelNodes.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import static org.truffleruby.language.dispatch.DispatchConfiguration.PUBLIC;
1414

1515
import java.io.File;
16-
import java.io.PrintStream;
1716
import java.nio.file.Paths;
1817
import java.util.ArrayList;
1918
import java.util.Arrays;
@@ -26,7 +25,6 @@
2625
import org.jcodings.specific.UTF8Encoding;
2726
import org.truffleruby.RubyContext;
2827
import org.truffleruby.RubyLanguage;
29-
import org.truffleruby.SuppressFBWarnings;
3028
import org.truffleruby.builtins.CoreMethod;
3129
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
3230
import org.truffleruby.builtins.CoreMethodNode;
@@ -1361,11 +1359,9 @@ protected Object p(VirtualFrame frame, Object value) {
13611359
return value;
13621360
}
13631361

1364-
@SuppressFBWarnings("OS")
13651362
@TruffleBoundary
13661363
private void print(Object inspected) {
1367-
final PrintStream stream = BacktraceFormatter.printStreamFor(getContext().getEnv().out());
1368-
stream.println(inspected.toString());
1364+
getContext().getEnvOutStream().println(inspected.toString());
13691365
}
13701366
}
13711367

src/main/java/org/truffleruby/language/backtrace/BacktraceFormatter.java

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
*/
1010
package org.truffleruby.language.backtrace;
1111

12-
import com.oracle.truffle.api.CompilerDirectives;
1312
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1413
import com.oracle.truffle.api.TruffleStackTrace;
1514
import com.oracle.truffle.api.TruffleStackTraceElement;
@@ -34,9 +33,7 @@
3433
import org.truffleruby.language.methods.TranslateExceptionNode;
3534
import org.truffleruby.parser.RubySource;
3635

37-
import java.io.OutputStream;
3836
import java.io.PrintStream;
39-
import java.io.UnsupportedEncodingException;
4037
import java.util.ArrayList;
4138
import java.util.EnumSet;
4239
import java.util.List;
@@ -113,7 +110,7 @@ public void printTopLevelRubyExceptionOnEnvStderr(RubyException rubyException) {
113110
@SuppressFBWarnings("OS")
114111
@TruffleBoundary
115112
public void printRubyExceptionOnEnvStderr(String info, RubyException rubyException) {
116-
final PrintStream printer = printStreamFor(context.getEnv().err());
113+
final PrintStream printer = context.getEnvErrStream();
117114
if (!info.isEmpty()) {
118115
printer.print(info);
119116
}
@@ -149,16 +146,7 @@ public void printRubyExceptionOnEnvStderr(String info, RubyException rubyExcepti
149146
@TruffleBoundary
150147
public void printBacktraceOnEnvStderr(Node currentNode) {
151148
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode);
152-
final PrintStream printer = printStreamFor(context.getEnv().err());
153-
printer.println(formatBacktrace(null, backtrace));
154-
}
155-
156-
public static PrintStream printStreamFor(OutputStream outputStream) {
157-
try {
158-
return new PrintStream(outputStream, true, "UTF-8");
159-
} catch (UnsupportedEncodingException e) {
160-
throw CompilerDirectives.shouldNotReachHere(e);
161-
}
149+
context.getEnvErrStream().println(formatBacktrace(null, backtrace));
162150
}
163151

164152
/** Format the backtrace as a String with \n between each line, but no trailing \n. */
@@ -396,7 +384,7 @@ public static void printInternalError(RubyContext context, Throwable throwable,
396384
throw ExceptionOperations.rethrow(throwable);
397385
}
398386

399-
final PrintStream stream = BacktraceFormatter.printStreamFor(context.getEnv().err());
387+
final PrintStream stream = context.getEnvErrStream();
400388
final BacktraceFormatter formatter = context.getDefaultBacktraceFormatter();
401389
stream.println();
402390
stream.println("truffleruby: " + from + ",");

0 commit comments

Comments
 (0)