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: 7187bb2ea2f3
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 483cd0d44bc3
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on Oct 14, 2015

  1. Copy the full SHA
    8afab97 View commit details
  2. [Truffle] Send a SystemExit back to the main thread if it reaches the…

    … thread top-level handler.
    eregon committed Oct 14, 2015
    Copy the full SHA
    483cd0d View commit details
28 changes: 28 additions & 0 deletions spec/ruby/shared/process/exit.rb
Original file line number Diff line number Diff line change
@@ -43,6 +43,34 @@
lambda { @object.exit([0]) }.should raise_error(TypeError)
lambda { @object.exit(nil) }.should raise_error(TypeError)
end

it "raises the SystemExit in the main thread if it reaches the top-level handler of another thread" do
ScratchPad.record []

ready = false
t = Thread.new {
Thread.pass until ready

begin
@object.exit 42
rescue SystemExit => e
ScratchPad << :in_thread
raise e
end
}

begin
ready = true
sleep
rescue SystemExit => e
ScratchPad << :in_main
end

ScratchPad.recorded.should == [:in_thread, :in_main]

# the thread also keeps the exception as its value
lambda { t.value }.should raise_error(SystemExit)
end
end

describe :process_exit!, shared: true do
20 changes: 14 additions & 6 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/FiberNodes.java
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ControlFlowException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;

@@ -22,6 +23,7 @@
import org.jruby.truffle.nodes.cast.SingleValueCastNodeGen;
import org.jruby.truffle.nodes.core.FiberNodesFactory.FiberTransferNodeFactory;
import org.jruby.truffle.nodes.methods.UnsupportedOperationBehavior;
import org.jruby.truffle.nodes.rubinius.ThreadPrimitiveNodes.ThreadRaisePrimitiveNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.BreakException;
import org.jruby.truffle.runtime.control.RaiseException;
@@ -57,15 +59,15 @@ private static DynamicObject createFiber(DynamicObject thread, DynamicObject rub
null);
}

public static void initialize(final RubyContext context, final DynamicObject fiber, final DynamicObject block) {
public static void initialize(final RubyContext context, final DynamicObject fiber, final DynamicObject block, final Node currentNode) {
assert RubyGuards.isRubyFiber(fiber);
assert RubyGuards.isRubyProc(block);
final String name = "Ruby Fiber@" + Layouts.PROC.getSharedMethodInfo(block).getSourceSection().getShortDescription();
Layouts.FIBER.setName(fiber, name);
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
handleFiberExceptions(context, fiber, block);
handleFiberExceptions(context, fiber, block, currentNode);
}
});
thread.setName(name);
@@ -81,11 +83,11 @@ public void run() {
}
}

private static void handleFiberExceptions(final RubyContext context, final DynamicObject fiber, final DynamicObject block) {
private static void handleFiberExceptions(final RubyContext context, final DynamicObject fiber, final DynamicObject block, Node currentNode) {
assert RubyGuards.isRubyFiber(fiber);
assert RubyGuards.isRubyProc(block);

run(context, fiber, new Runnable() {
run(context, fiber, currentNode, new Runnable() {
@Override
public void run() {
try {
@@ -112,12 +114,18 @@ public void run() {
});
}

public static void run(RubyContext context, DynamicObject fiber, final Runnable task) {
public static void run(RubyContext context, DynamicObject fiber, Node currentNode, final Runnable task) {
assert RubyGuards.isRubyFiber(fiber);

start(context, fiber);
try {
task.run();
} catch (RaiseException e) {
if (Layouts.BASIC_OBJECT.getLogicalClass(e.getRubyException()) == context.getCoreLibrary().getSystemExitClass()) {
// SystemExit: send it to the main thread if it reached here
ThreadRaisePrimitiveNode.raiseInThread(context, context.getThreadManager().getRootThread(), e.getRubyException(), currentNode);
}
throw e;
} finally {
cleanup(context, fiber);
}
@@ -257,7 +265,7 @@ public InitializeNode(RubyContext context, SourceSection sourceSection) {
public DynamicObject initialize(DynamicObject fiber, DynamicObject block) {
CompilerDirectives.transferToInterpreter();

FiberNodes.initialize(getContext(), fiber, block);
FiberNodes.initialize(getContext(), fiber, block, this);
return nil();
}

Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.rubinius.ThreadPrimitiveNodes.ThreadRaisePrimitiveNode;
import org.jruby.truffle.runtime.NotProvided;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
@@ -30,8 +31,6 @@
import org.jruby.truffle.runtime.subsystems.FiberManager;
import org.jruby.truffle.runtime.subsystems.SafepointAction;
import org.jruby.truffle.runtime.subsystems.ThreadManager;
import org.jruby.util.StringSupport;

import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -91,7 +90,7 @@ public static void run(DynamicObject thread, final RubyContext context, Node cur
start(context, thread);
try {
DynamicObject fiber = Layouts.THREAD.getFiberManager(thread).getRootFiber();
FiberNodes.run(context, fiber, task);
FiberNodes.run(context, fiber, currentNode, task);
} catch (ThreadExitException e) {
Layouts.THREAD.setValue(thread, context.getCoreLibrary().getNilObject());
return;
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.truffle.runtime.RubyCallStack;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.backtrace.Backtrace;
@@ -36,9 +37,14 @@ public ThreadRaisePrimitiveNode(RubyContext context, SourceSection sourceSection

@Specialization(guards = { "isRubyThread(thread)", "isRubyException(exception)" })
public DynamicObject raise(DynamicObject thread, final DynamicObject exception) {
final Thread javaThread = Layouts.FIBER.getThread((Layouts.THREAD.getFiberManager(thread).getCurrentFiber()));
raiseInThread(getContext(), thread, exception, this);
return nil();
}

getContext().getSafepointManager().pauseThreadAndExecuteLater(javaThread, this, new SafepointAction() {
public static void raiseInThread(RubyContext context, DynamicObject rubyThread, final DynamicObject exception, Node currentNode) {
final Thread javaThread = Layouts.FIBER.getThread((Layouts.THREAD.getFiberManager(rubyThread).getCurrentFiber()));

context.getSafepointManager().pauseThreadAndExecuteLater(javaThread, currentNode, new SafepointAction() {
@Override
public void run(DynamicObject currentThread, Node currentNode) {
if (Layouts.EXCEPTION.getBacktrace(exception) == null) {
@@ -48,7 +54,6 @@ public void run(DynamicObject currentThread, Node currentNode) {
throw new RaiseException(exception);
}
});
return nil();
}

}
Original file line number Diff line number Diff line change
@@ -17,7 +17,6 @@
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.RubyThread.Status;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.core.InterruptMode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
@@ -181,7 +180,7 @@ private void pauseAllThreadsAndExecute(Node currentNode, boolean isRubyThread, S
assumptionInvalidated(currentNode, true);
}

public void pauseThreadAndExecuteLater(final Thread thread, RubyNode currentNode, final SafepointAction action) {
public void pauseThreadAndExecuteLater(final Thread thread, Node currentNode, final SafepointAction action) {
if (Thread.currentThread() == thread) {
// fast path if we are already the right thread
DynamicObject rubyThread = context.getThreadManager().getCurrentThread();