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: 4142c8d58e59
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 07a515fed2f9
Choose a head ref
  • 6 commits
  • 7 files changed
  • 1 contributor

Commits on Jan 15, 2015

  1. Copy the full SHA
    ac7afc8 View commit details
  2. [Truffle] Execute signal handlers on the Ruby root thread.

    * Allows to Ctrl+C in MSpec for instance.
    eregon committed Jan 15, 2015
    Copy the full SHA
    2aa09ac View commit details
  3. [Truffle] Pass the current thread in SafepointManager.

    * Clearer intent on the action being run by a RubyThread.
    * More practical for most conditions, the old condition can be
      found by checking thread identity with the value of
      getCurrentThread() before calling pauseAllThreadsAndExecute().
    eregon committed Jan 15, 2015
    Copy the full SHA
    5b18ff3 View commit details
  4. [Truffle] Use synchronized over an explicit ReentrantLock.

    * Avoids extra nesting and nested try-catch blocks.
    eregon committed Jan 15, 2015
    Copy the full SHA
    8003abd View commit details
  5. Copy the full SHA
    5e686d5 View commit details
  6. Copy the full SHA
    07a515f View commit details
25 changes: 19 additions & 6 deletions core/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java
Original file line number Diff line number Diff line change
@@ -732,7 +732,7 @@ public Object exit(int exitCode) {

}

@CoreMethod(names = "exit!", isModuleFunction = true)
@CoreMethod(names = "exit!", isModuleFunction = true, optional = 1)
public abstract static class ExitBangNode extends CoreMethodNode {

public ExitBangNode(RubyContext context, SourceSection sourceSection) {
@@ -744,11 +744,17 @@ public ExitBangNode(ExitBangNode prev) {
}

@Specialization
public RubyNilClass exit() {
public RubyNilClass exit(UndefinedPlaceholder exitCode) {
return exit(1);
}

@Specialization
public RubyNilClass exit(int exitCode) {
CompilerDirectives.transferToInterpreter();
System.exit(1);
System.exit(exitCode);
return getContext().getCoreLibrary().getNilObject();
}

}

@CoreMethod(names = "extend", argumentsAsArray = true, required = 1)
@@ -1430,12 +1436,18 @@ public PrintNode(PrintNode prev) {
}

@Specialization
public RubyNilClass print(final VirtualFrame frame, final Object[] args) {
public RubyNilClass print(VirtualFrame frame, Object[] args) {
final byte[][] bytes = new byte[args.length][];

for (int i = 0; i < args.length; i++) {
bytes[i] = ((RubyString) toS.call(frame, args[i], "to_s", null)).getBytes().bytes();
}

final RubyThread runningThread = getContext().getThreadManager().leaveGlobalLock();

try {
for (Object arg : args) {
write(((RubyString) toS.call(frame, arg, "to_s", null)).getBytes().bytes());
for (byte[] string : bytes) {
write(string);
}
} finally {
getContext().getThreadManager().enterGlobalLock(runningThread);
@@ -2009,6 +2021,7 @@ public RubyString sprintf(Object[] args) {
final RubyThread runningThread = getContext().getThreadManager().leaveGlobalLock();

try {
// TODO(CS): this is only safe if values' toString() are pure.
StringFormatter.format(getContext(), printStream, format, values);
} finally {
getContext().getThreadManager().enterGlobalLock(runningThread);
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ public Object trap(RubyString signalName, UndefinedPlaceholder command, final Ru

final Signal signal = new Signal(signalName.toString());

final SignalHandler newHandler = new ProcSignalHandler(block);
final SignalHandler newHandler = new ProcSignalHandler(getContext(), block);
final SignalHandler oldHandler = Signal.handle(signal, newHandler);

if (oldHandler instanceof ProcSignalHandler) {
12 changes: 6 additions & 6 deletions core/src/main/java/org/jruby/truffle/nodes/core/ThreadNodes.java
Original file line number Diff line number Diff line change
@@ -114,11 +114,11 @@ public KillNode(KillNode prev) {

@Specialization
public RubyThread kill(final RubyThread thread) {
getContext().getSafepointManager().pauseAllThreadsAndExecute(new Consumer<Boolean>() {
getContext().getSafepointManager().pauseAllThreadsAndExecute(new Consumer<RubyThread>() {

@Override
public void accept(Boolean isPausingThread) {
if (getContext().getThreadManager().getCurrentThread() == thread) {
public void accept(RubyThread currentThread) {
if (currentThread == thread) {
throw new ThreadExitException();
}
}
@@ -230,11 +230,11 @@ public RubyNilClass raise(VirtualFrame frame, final RubyThread thread, RubyClass

final RaiseException exceptionWrapper = new RaiseException((RubyException) exception);

getContext().getSafepointManager().pauseAllThreadsAndExecute(new Consumer<Boolean>() {
getContext().getSafepointManager().pauseAllThreadsAndExecute(new Consumer<RubyThread>() {

@Override
public void accept(Boolean isPausingThread) {
if (getContext().getThreadManager().getCurrentThread() == thread) {
public void accept(RubyThread currentThread) {
if (currentThread == thread) {
throw exceptionWrapper;
}
}
Original file line number Diff line number Diff line change
@@ -9,21 +9,38 @@
*/
package org.jruby.truffle.runtime.signal;

import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyProc;
import org.jruby.truffle.runtime.core.RubyThread;
import org.jruby.truffle.runtime.subsystems.ThreadManager;
import org.jruby.truffle.runtime.util.Consumer;

import sun.misc.Signal;
import sun.misc.SignalHandler;

public class ProcSignalHandler implements SignalHandler {

private final RubyContext context;
private final RubyProc proc;

public ProcSignalHandler(RubyProc proc) {
public ProcSignalHandler(RubyContext context, RubyProc proc) {
this.context = context;
this.proc = proc;
}

@Override
public void handle(Signal signal) {
proc.rootCall();
final ThreadManager threadManager = context.getThreadManager();
context.getSafepointManager().pauseAllThreadsAndExecuteSignalHandler(new Consumer<RubyThread>() {

public void accept(RubyThread currentThread) {
if (currentThread == threadManager.getRootThread()) {
proc.rootCall();
}
}

});

}

public RubyProc getProc() {
Original file line number Diff line number Diff line change
@@ -219,10 +219,10 @@ public boolean visit(RubyBasicObject object) {

};

context.getSafepointManager().pauseAllThreadsAndExecute(new Consumer<Boolean>() {
context.getSafepointManager().pauseAllThreadsAndExecute(new Consumer<RubyThread>() {

@Override
public void accept(Boolean isPausingThread) {
public void accept(RubyThread currentThread) {
synchronized (liveObjects) {
context.getCoreLibrary().getGlobalVariablesObject().visitObjectGraph(visitor);
context.getCoreLibrary().getMainObject().visitObjectGraph(visitor);
Original file line number Diff line number Diff line change
@@ -27,39 +27,27 @@ public class SafepointManager {

private final RubyContext context;

private final Lock lock = new ReentrantLock();
@CompilerDirectives.CompilationFinal private Assumption assumption = Truffle.getRuntime().createAssumption();
private CyclicBarrier barrier;
private int liveThreads = 1;
private Consumer<Boolean> action;
private Consumer<RubyThread> action;

public SafepointManager(RubyContext context) {
this.context = context;
}

public void enterThread() {
public synchronized void enterThread() {
CompilerAsserts.neverPartOfCompilation();

try {
lock.lock();
liveThreads++;
} finally {
lock.unlock();
}
liveThreads++;
}

public void leaveThread() {
public synchronized void leaveThread() {
CompilerAsserts.neverPartOfCompilation();

try {
lock.lock();

poll();
poll();

liveThreads--;
} finally {
lock.unlock();
}
liveThreads--;
}

public void poll() {
@@ -74,54 +62,74 @@ private void assumptionInvalidated() {
waitOnBarrier();

try {
action.accept(false);
action.accept(context.getThreadManager().getCurrentThread());
} finally {
waitOnBarrier();
}
}

public void pauseAllThreadsAndExecute(final Consumer<Boolean> action) {
public synchronized void pauseAllThreadsAndExecute(final Consumer<RubyThread> action) {
CompilerDirectives.transferToInterpreter();

try {
lock.lock();

SafepointManager.this.action = action;
SafepointManager.this.action = action;

barrier = new CyclicBarrier(liveThreads);
barrier = new CyclicBarrier(liveThreads);

assumption.invalidate();
assumption.invalidate();

waitOnBarrier();
// wait for all threads to reach their safepoint
waitOnBarrier();

assumption = Truffle.getRuntime().createAssumption();
assumption = Truffle.getRuntime().createAssumption();

try {
action.accept(true);
} finally {
waitOnBarrier();
}
try {
action.accept(context.getThreadManager().getCurrentThread());
} finally {
lock.unlock();
// wait for all threads to execute the action
waitOnBarrier();
}
}

public synchronized void pauseAllThreadsAndExecuteSignalHandler(final Consumer<RubyThread> action) {
CompilerDirectives.transferToInterpreter();

// The current (Java) thread is not a Ruby thread, so we do not touch the global lock.

SafepointManager.this.action = action;

barrier = new CyclicBarrier(liveThreads + 1);

assumption.invalidate();

// wait for all threads to reach their safepoint
waitOnBarrierNoGlobalLock();

assumption = Truffle.getRuntime().createAssumption();

// wait for all Ruby threads to execute the action
waitOnBarrierNoGlobalLock();
}

private void waitOnBarrier() {
final RubyThread runningThread = context.getThreadManager().leaveGlobalLock();

try {
while (true) {
try {
barrier.await();
break;
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
}
}
waitOnBarrierNoGlobalLock();
} finally {
context.getThreadManager().enterGlobalLock(runningThread);
}
}

private void waitOnBarrierNoGlobalLock() {
while (true) {
try {
barrier.await();
break;
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
}
}
}

}
Original file line number Diff line number Diff line change
@@ -36,6 +36,10 @@ public ThreadManager(RubyContext context) {
enterGlobalLock(rootThread);
}

public RubyThread getRootThread() {
return rootThread;
}

/**
* Enters the global lock. Reentrant, but be aware that Ruby threads are not one-to-one with
* Java threads. Needs to be told which Ruby thread is becoming active as it can't work this out
@@ -69,16 +73,12 @@ public RubyThread getCurrentThread() {
return currentThread;
}

public void registerThread(RubyThread thread) {
synchronized (this) {
runningThreads.add(thread);
}
public synchronized void registerThread(RubyThread thread) {
runningThreads.add(thread);
}

public void unregisterThread(RubyThread thread) {
synchronized (this) {
runningThreads.remove(thread);
}
public synchronized void unregisterThread(RubyThread thread) {
runningThreads.remove(thread);
}

public void shutdown() {