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

Commits on Jan 19, 2015

  1. Copy the full SHA
    cc4049a View commit details
  2. Copy the full SHA
    d75119d View commit details
  3. [Truffle] Explicitly mentions the Specialization throws an Arithmetic…

    …Exception.
    
    * Avoids a Truffle DSL processor bug.
    eregon committed Jan 19, 2015
    Copy the full SHA
    dbb166d View commit details
  4. [Truffle] Add two abstractions in ThreadManager to run interruptible …

    …methods.
    
    * Most cases will want to repeat the action if it was interrupted.
    eregon committed Jan 19, 2015
    Copy the full SHA
    92201a2 View commit details
  5. [Truffle] Release GIL in Kernel#gets and use a BufferedReader.

    * Simpler to use and same functionality.
    * STDIN can only be interrupted after pressing RETURN in normal Java,
      and there is no reasonable/easy way to make a SelectableChannel for STDIN (JRuby uses FFI).
    eregon committed Jan 19, 2015
    Copy the full SHA
    295fdb5 View commit details
Original file line number Diff line number Diff line change
@@ -111,7 +111,7 @@ public long objectID(int value) {
}

@Specialization(rewriteOn = ArithmeticException.class)
public long objectIDSmallFixnumOverflow(long value) {
public long objectIDSmallFixnumOverflow(long value) throws ArithmeticException {
return ObjectIDOperations.smallFixnumToIDOverflow(value);
}

84 changes: 31 additions & 53 deletions core/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
import com.oracle.truffle.api.frame.*;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.common.IRubyWarnings;
import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyNode;
@@ -40,6 +41,7 @@
import org.jruby.truffle.runtime.hash.HashOperations;
import org.jruby.truffle.runtime.hash.KeyValue;
import org.jruby.truffle.runtime.methods.InternalMethod;
import org.jruby.truffle.runtime.subsystems.ThreadManager.BlockingActionWithoutGlobalLock;
import org.jruby.util.ByteList;
import org.jruby.util.cli.Options;

@@ -676,7 +678,7 @@ private static void exec(RubyContext context, String[] commandLine) {
builder.environment().put(keyValue.getKey().toString(), keyValue.getValue().toString());
}

Process process;
final Process process;

try {
process = builder.start();
@@ -685,16 +687,12 @@ private static void exec(RubyContext context, String[] commandLine) {
throw new RuntimeException(e);
}

int exitCode;

while (true) {
try {
exitCode = process.waitFor();
break;
} catch (InterruptedException e) {
continue;
int exitCode = context.getThreadManager().runUntilResult(new BlockingActionWithoutGlobalLock<Integer>() {
@Override
public Integer block() throws InterruptedException {
return process.waitFor();
}
}
});

System.exit(exitCode);
}
@@ -714,11 +712,7 @@ public ExitNode(ExitNode prev) {

@Specialization
public Object exit(UndefinedPlaceholder exitCode) {
notDesignedForCompilation();

getContext().shutdown();
System.exit(0);
return null;
return exit(0);
}

@Specialization
@@ -857,26 +851,24 @@ public GetsNode(GetsNode prev) {
public RubyString gets(VirtualFrame frame) {
notDesignedForCompilation();

final RubyContext context = getContext();

final Frame caller = Truffle.getRuntime().getCallerFrame().getFrame(FrameInstance.FrameAccess.READ_WRITE, false);
// TODO(CS): having some trouble interacting with JRuby stdin - so using this hack
final InputStream in = getContext().getRuntime().getInstanceConfig().getInput();

final String line;
final BufferedReader reader = new BufferedReader(new InputStreamReader(in));

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

try {
line = gets(context);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
getContext().getThreadManager().enterGlobalLock(runningThread);
}
final String line = getContext().getThreadManager().runOnce(new BlockingActionWithoutGlobalLock<String>() {
@Override
public String block() throws InterruptedException {
return gets(reader);
}
});

final RubyString rubyLine = context.makeString(line);
final RubyString rubyLine = getContext().makeString(line);

// Set the local variable $_ in the caller

final Frame caller = Truffle.getRuntime().getCallerFrame().getFrame(FrameInstance.FrameAccess.READ_WRITE, false);

final FrameSlot slot = caller.getFrameDescriptor().findFrameSlot("$_");

if (slot != null) {
@@ -887,22 +879,12 @@ public RubyString gets(VirtualFrame frame) {
}

@TruffleBoundary
private static String gets(RubyContext context) throws IOException {
// TODO(CS): having some trouble interacting with JRuby stdin - so using this hack

final StringBuilder builder = new StringBuilder();

while (true) {
final int c = context.getRuntime().getInstanceConfig().getInput().read();

if (c == -1 || c == '\r' || c == '\n') {
break;
}

builder.append((char) c);
private static String gets(BufferedReader reader) throws InterruptedException {
try {
return reader.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}

return builder.toString();
}

}
@@ -1970,17 +1952,13 @@ public double sleep(double duration) {
private double doSleep(final double duration) {
final long start = System.nanoTime();

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

try {
try {
getContext().getThreadManager().runOnce(new BlockingActionWithoutGlobalLock<Boolean>() {
@Override
public Boolean block() throws InterruptedException {
Thread.sleep((long) (duration * 1000));
} finally {
getContext().getThreadManager().enterGlobalLock(runningThread);
return SUCCESS;
}
} catch (InterruptedException e) {
getContext().getSafepointManager().poll();
}
});

final long end = System.nanoTime();

Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ public static boolean isSmallFixnum(long fixnum) {
return SMALL_FIXNUM_MIN <= fixnum && fixnum <= SMALL_FIXNUM_MAX;
}

public static long smallFixnumToIDOverflow(long fixnum) throws ArithmeticException{
public static long smallFixnumToIDOverflow(long fixnum) throws ArithmeticException {
return ExactMath.addExact(ExactMath.multiplyExact(fixnum, 2), 1);
}

18 changes: 8 additions & 10 deletions core/src/main/java/org/jruby/truffle/runtime/core/RubyFiber.java
Original file line number Diff line number Diff line change
@@ -10,11 +10,13 @@
package org.jruby.truffle.runtime.core;

import com.oracle.truffle.api.nodes.ControlFlowException;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.objects.Allocator;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.subsystems.FiberManager;
import org.jruby.truffle.runtime.subsystems.ThreadManager;
import org.jruby.truffle.runtime.subsystems.ThreadManager.BlockingActionWithoutGlobalLock;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
@@ -108,22 +110,18 @@ public void run() {
}

/**
* Send the Java thread that represents this fiber to sleep until it recieves a resume or exit
* Send the Java thread that represents this fiber to sleep until it receives a resume or exit
* message. On entry, assumes that the GIL is not held. On exit, holding the GIL.
*/
public Object waitForResume() {
RubyNode.notDesignedForCompilation();

FiberMessage message = null;

do {
try {
// TODO(cs) what is a suitable timeout?
message = messageQueue.poll(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// Poll again
FiberMessage message = getContext().getThreadManager().runUntilResult(new BlockingActionWithoutGlobalLock<FiberMessage>() {
@Override
public FiberMessage block() throws InterruptedException {
return messageQueue.poll(1, TimeUnit.SECONDS);
}
} while (message == null);
});

if (message instanceof FiberExitMessage) {
throw new FiberExitException();
20 changes: 7 additions & 13 deletions core/src/main/java/org/jruby/truffle/runtime/core/RubyThread.java
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
import org.jruby.truffle.runtime.control.ReturnException;
import org.jruby.truffle.runtime.control.ThreadExitException;
import org.jruby.truffle.runtime.subsystems.ThreadManager;
import org.jruby.truffle.runtime.subsystems.ThreadManager.BlockingActionWithoutGlobalLock;

import java.util.concurrent.CountDownLatch;

@@ -108,20 +109,13 @@ public void setRootThread(Thread thread) {
}

public void join() {
final RubyThread runningThread = getContext().getThreadManager().leaveGlobalLock();

try {
while (true) {
try {
finished.await();
break;
} catch (InterruptedException e) {
// Await again
}
getContext().getThreadManager().runUntilResult(new BlockingActionWithoutGlobalLock<Boolean>() {
@Override
public Boolean block() throws InterruptedException {
finished.await();
return SUCCESS;
}
} finally {
getContext().getThreadManager().enterGlobalLock(runningThread);
}
});

if (exception != null) {
throw new RaiseException(exception);
Original file line number Diff line number Diff line change
@@ -14,12 +14,14 @@
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.FrameInstanceVisitor;
import com.oracle.truffle.api.frame.FrameSlot;

import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBasicObject;
import org.jruby.truffle.runtime.core.RubyProc;
import org.jruby.truffle.runtime.core.RubyThread;
import org.jruby.truffle.runtime.subsystems.ThreadManager.BlockingActionWithoutGlobalLock;
import org.jruby.truffle.runtime.util.Consumer;

import java.lang.ref.ReferenceQueue;
@@ -132,18 +134,18 @@ private void runFinalizers() {

// Leave the global lock and wait on the finalizer queue

final RubyThread runningThread = context.getThreadManager().leaveGlobalLock();
finalizerJavaThread = Thread.currentThread();

try {
finalizerReference = (FinalizerReference) finalizerQueue.remove();
} catch (InterruptedException e) {
continue;
} finally {
context.getThreadManager().enterGlobalLock(runningThread);
}
finalizerReference = context.getThreadManager().runOnce(new BlockingActionWithoutGlobalLock<FinalizerReference>() {
@Override
public FinalizerReference block() throws InterruptedException {
return (FinalizerReference) finalizerQueue.remove();
}
});

runFinalizers(finalizerReference);
if (finalizerReference != null) {
runFinalizers(finalizerReference);
}
}

finished.countDown();
@@ -162,6 +164,7 @@ private static void runFinalizers(FinalizerReference finalizerReference) {
public void shutdown() {
RubyNode.notDesignedForCompilation();

// TODO (eregon): refactor this without explicit interrupt
context.getThreadManager().enterGlobalLock(finalizerThread);

try {
@@ -174,14 +177,13 @@ public void shutdown() {
finalizerJavaThread.interrupt();
}

context.getThreadManager().leaveGlobalLock();

try {
finished.await();
} catch (InterruptedException e) {
} finally {
context.getThreadManager().enterGlobalLock(finalizerThread);
}
context.getThreadManager().runOnce(new BlockingActionWithoutGlobalLock<Boolean>() {
@Override
public Boolean block() throws InterruptedException {
finished.await();
return SUCCESS;
}
});
}

// Run any finalizers for objects that are still live
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;

import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyThread;
import org.jruby.truffle.runtime.util.Consumer;
@@ -135,6 +136,7 @@ private void waitOnBarrierNoGlobalLock() {
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException("Should not be interrupted while waiting on the safepoint barrier", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -71,6 +71,58 @@ public RubyThread leaveGlobalLock() {
return result;
}

public static interface BlockingActionWithoutGlobalLock<T> {
public static boolean SUCCESS = true;

T block() throws InterruptedException;
}

/**
* Runs {@code action} until it returns a non-null value.
* The action might be {@link Thread#interrupted()}, for instance by
* the {@link SafepointManager}, in which case it will be run again.
*
* @param action must not touch any Ruby state
* @return the first non-null return value from {@code action}
*/
@CompilerDirectives.TruffleBoundary
public <T> T runUntilResult(BlockingActionWithoutGlobalLock<T> action) {
T result = null;

do {
result = runOnce(action);
} while (result == null);

return result;
}

/**
* Runs {@code action} once.
* The action might be {@link Thread#interrupted()}, for instance by
* the {@link SafepointManager}, in which case null will be returned.
*
* @param action must not touch any Ruby state
* @return the return value from {@code action} or null if interrupted
*/
@CompilerDirectives.TruffleBoundary
public <T> T runOnce(BlockingActionWithoutGlobalLock<T> action) {
T result = null;
final RubyThread runningThread = leaveGlobalLock();

try {
try {
result = action.block();
} finally {
// We need to enter the global lock before anything else!
enterGlobalLock(runningThread);
}
} catch (InterruptedException e) {
// We were interrupted, possibly by the SafepointManager.
runningThread.getContext().getSafepointManager().poll();
}
return result;
}

public RubyThread getCurrentThread() {
return currentThread;
}