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
    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)