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

Commits on Mar 29, 2016

  1. Wait for the main thread to be interrupted in Thread#abort_on_excepti…

    …on spec
    
    * Thread#join would also raise the exception which is not what we want to test.
    eregon committed Mar 29, 2016

    Verified

    This commit was signed with the committer’s verified signature.
    makenowjust Hiroya Fujinami
    Copy the full SHA
    738177f View commit details
  2. [Truffle] Fix Thread[.#]abort_on_exception.

    * Only leave the SafepointManager after processing errors.
    * Simpler control flow in ThreadNodes.run.
    * Inherit global abort_on_exception.
    eregon committed Mar 29, 2016
    Copy the full SHA
    401a05b View commit details
7 changes: 4 additions & 3 deletions spec/ruby/core/thread/abort_on_exception_spec.rb
Original file line number Diff line number Diff line change
@@ -34,11 +34,12 @@
begin
ScratchPad << :before

@thread.abort_on_exception = true if @object
lambda do
@thread.abort_on_exception = true if @object
ThreadSpecs.state = :run
@thread.join
end.should raise_error(RuntimeError)
# Wait for the main thread to be interrupted
Thread.pass while @thread.alive?
end.should raise_error(RuntimeError, "Thread#abort_on_exception= specs")

ScratchPad << :after
rescue Object
3 changes: 3 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/CoreLibrary.java
Original file line number Diff line number Diff line change
@@ -445,8 +445,11 @@ public CoreLibrary(RubyContext context) {
Layouts.CLASS.setInstanceFactoryUnsafe(stringClass, stringFactory);
symbolClass = defineClass("Symbol");
Layouts.CLASS.setInstanceFactoryUnsafe(symbolClass, alwaysFrozen(Layouts.SYMBOL.createSymbolShape(symbolClass, symbolClass)));

threadClass = defineClass("Thread");
threadClass.define("@abort_on_exception", false);
Layouts.CLASS.setInstanceFactoryUnsafe(threadClass, Layouts.THREAD.createThreadShape(threadClass, threadClass));

threadBacktraceClass = defineClass(threadClass, objectClass, "Backtrace");
threadBacktraceLocationClass = defineClass(threadBacktraceClass, objectClass, "Location");
Layouts.CLASS.setInstanceFactoryUnsafe(threadBacktraceLocationClass, ThreadBacktraceLocationLayoutImpl.INSTANCE.createThreadBacktraceLocationShape(threadBacktraceLocationClass, threadBacktraceLocationClass));
Original file line number Diff line number Diff line change
@@ -118,24 +118,17 @@ public void run() {
});
}

public static void run(RubyContext context, DynamicObject fiber, Node currentNode, final Runnable task) {
private 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.getException()) == context.getCoreLibrary().getSystemExitClass()) {
// SystemExit: send it to the main thread if it reached here
ThreadRaisePrimitiveNode.raiseInThread(context, context.getThreadManager().getRootThread(), e.getException(), currentNode);
}
throw e;
} finally {
cleanup(context, fiber);
}
}

// Only used by the main thread which cannot easily wrap everything inside a try/finally.
public static void start(RubyContext context, DynamicObject fiber) {
assert RubyGuards.isRubyFiber(fiber);
Layouts.FIBER.setThread(fiber, Thread.currentThread());
@@ -146,7 +139,6 @@ public static void start(RubyContext context, DynamicObject fiber) {
Layouts.FIBER.getInitializedLatch(fiber).countDown();
}

// Only used by the main thread which cannot easily wrap everything inside a try/finally.
public static void cleanup(RubyContext context, DynamicObject fiber) {
assert RubyGuards.isRubyFiber(fiber);
Layouts.FIBER.setAlive(fiber, false);
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@
import org.jruby.truffle.core.fiber.FiberManager;
import org.jruby.truffle.core.fiber.FiberNodes;
import org.jruby.truffle.core.proc.ProcNodes;
import org.jruby.truffle.core.rubinius.ThreadPrimitiveNodes.ThreadRaisePrimitiveNode;
import org.jruby.truffle.language.NotProvided;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;
@@ -39,7 +40,6 @@
import org.jruby.truffle.language.control.RaiseException;
import org.jruby.truffle.language.control.ReturnException;
import org.jruby.truffle.language.control.ThreadExitException;

import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -60,7 +60,7 @@ public static DynamicObject createRubyThread(RubyContext context, DynamicObject
null,
null,
new CountDownLatch(1),
false,
getGlobalAbortOnException(context),
null,
null,
null,
@@ -70,6 +70,11 @@ public static DynamicObject createRubyThread(RubyContext context, DynamicObject
return object;
}

public static boolean getGlobalAbortOnException(RubyContext context) {
final DynamicObject threadClass = context.getCoreLibrary().getThreadClass();
return (boolean) threadClass.get("@abort_on_exception");
}

private static DynamicObject createThreadLocals(RubyContext context) {
final DynamicObjectFactory instanceFactory = Layouts.CLASS.getInstanceFactory(context.getCoreLibrary().getObjectClass());
final DynamicObject threadLocals = Layouts.BASIC_OBJECT.createBasicObject(instanceFactory);
@@ -108,23 +113,34 @@ public static void run(DynamicObject thread, final RubyContext context, Node cur
final String name = "Ruby Thread@" + info;
Layouts.THREAD.setNameUnsafe(thread, name);
Thread.currentThread().setName(name);
DynamicObject fiber = Layouts.THREAD.getFiberManager(thread).getRootFiber();

start(context, thread);
FiberNodes.start(context, fiber);
try {
DynamicObject fiber = Layouts.THREAD.getFiberManager(thread).getRootFiber();
FiberNodes.run(context, fiber, currentNode, task);
task.run();
} catch (ThreadExitException e) {
Layouts.THREAD.setValue(thread, context.getCoreLibrary().getNilObject());
return;
} catch (RaiseException e) {
Layouts.THREAD.setException(thread, e.getException());
setException(context, thread, e.getException(), currentNode);
} catch (ReturnException e) {
Layouts.THREAD.setException(thread, context.getCoreLibrary().unexpectedReturn(currentNode));
setException(context, thread, context.getCoreLibrary().unexpectedReturn(currentNode), currentNode);
} finally {
FiberNodes.cleanup(context, fiber);
cleanup(context, thread);
}
}

private static void setException(RubyContext context, DynamicObject thread, DynamicObject exception, Node currentNode) {
final DynamicObject mainThread = context.getThreadManager().getRootThread();
final boolean isSystemExit = Layouts.BASIC_OBJECT.getLogicalClass(exception) == context.getCoreLibrary().getSystemExitClass();
if (thread != mainThread && (isSystemExit || Layouts.THREAD.getAbortOnException(thread))) {
ThreadRaisePrimitiveNode.raiseInThread(context, mainThread, exception, currentNode);
}
Layouts.THREAD.setException(thread, exception);
}

public static void start(RubyContext context, DynamicObject thread) {
assert RubyGuards.isRubyThread(thread);
Layouts.THREAD.setThread(thread, Thread.currentThread());
6 changes: 4 additions & 2 deletions truffle/src/main/ruby/core/thread.rb
Original file line number Diff line number Diff line change
@@ -41,12 +41,14 @@ def self.start(*args, &block)
Thread.new(*args, &block)
end

@abort_on_exception = false

def self.abort_on_exception
current.abort_on_exception
@abort_on_exception
end

def self.abort_on_exception=(value)
current.abort_on_exception = value
@abort_on_exception = value
end

def self.handle_interrupt(config, &block)