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

Commits on Feb 1, 2015

  1. Copy the full SHA
    dd943c7 View commit details
  2. Copy the full SHA
    e63c5e0 View commit details

Commits on Feb 2, 2015

  1. Copy the full SHA
    c420b1d View commit details
5 changes: 0 additions & 5 deletions spec/truffle/tags/core/fiber/new_tags.txt

This file was deleted.

12 changes: 0 additions & 12 deletions spec/truffle/tags/core/fiber/resume_tags.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1 @@
fails:Fiber#resume can be invoked from the root Fiber
fails:Fiber#resume passes control to the beginning of the block on first invocation
fails:Fiber#resume returns the last value encountered on first invocation
fails:Fiber#resume runs until the end of the block
fails:Fiber#resume runs until Fiber.yield
fails:Fiber#resume resumes from the last call to Fiber.yield on subsequent invocations
fails:Fiber#resume accepts any number of arguments
fails:Fiber#resume sets the block parameters to its arguments on the first invocation
fails:Fiber#resume raises a FiberError if the Fiber is dead
fails:Fiber#resume raises a LocalJumpError if the block includes a return statement
fails:Fiber#resume raises a LocalJumpError if the block includes a break statement
fails:Fiber#resume returns control to the calling Fiber if called from one
fails:Fiber#resume executes the ensure clause
5 changes: 0 additions & 5 deletions spec/truffle/tags/core/fiber/yield_tags.txt

This file was deleted.

2 changes: 1 addition & 1 deletion spec/truffle/truffle.mspec
Original file line number Diff line number Diff line change
@@ -104,7 +104,7 @@ class MSpecScript

MSpec.enable_feature :encoding
MSpec.enable_feature :fiber
MSpec.enable_feature :fork
MSpec.disable_feature :fork
MSpec.enable_feature :generator

set :files, get(:language) + get(:core) + get(:rubysl)
Original file line number Diff line number Diff line change
@@ -72,6 +72,11 @@ public AddNode(AddNode prev) {
super(prev);
}

@Specialization(guards = {"isNull", "isOtherNull"})
public RubyArray addNull(RubyArray a, RubyArray b) {
return new RubyArray(getContext().getCoreLibrary().getArrayClass(), null, 0);
}

@Specialization(guards = {"isObject", "isOtherNull"})
public RubyArray addObjectNull(RubyArray a, RubyArray b) {
return new RubyArray(getContext().getCoreLibrary().getArrayClass(), Arrays.copyOf((Object[]) a.getStore(), a.getSize()), a.getSize());
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@

import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.methods.UnsupportedOperationBehavior;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.RubyFiber;
@@ -36,7 +37,7 @@ public Object resume(RubyFiber fiberBeingResumed, Object[] args) {
notDesignedForCompilation();

if (!fiberBeingResumed.isAlive()) {
throw new RaiseException(getContext().getCoreLibrary().fiberErrorDeadFiberCalled(this));
throw new RaiseException(getContext().getCoreLibrary().deadFiberCalledError(this));
}

final RubyFiber sendingFiber = getContext().getFiberManager().getCurrentFiber();
@@ -48,7 +49,7 @@ public Object resume(RubyFiber fiberBeingResumed, Object[] args) {

}

@CoreMethod(names = "initialize", needsBlock = true)
@CoreMethod(names = "initialize", needsBlock = true, unsupportedOperationBehavior = UnsupportedOperationBehavior.ARGUMENT_ERROR)
public abstract static class InitializeNode extends CoreMethodNode {

public InitializeNode(RubyContext context, SourceSection sourceSection) {
@@ -87,6 +88,10 @@ public Object yield(Object[] args) {
final RubyFiber yieldingFiber = getContext().getFiberManager().getCurrentFiber();
final RubyFiber fiberYieldedTo = yieldingFiber.getLastResumedByFiber();

if (yieldingFiber.isTopLevel() || fiberYieldedTo == null) {
throw new RaiseException(getContext().getCoreLibrary().yieldFromRootFiberError(this));
}

fiberYieldedTo.resume(yieldingFiber, args);

return yieldingFiber.waitForResume();
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ public Object execute(VirtualFrame frame) {
return body.execute(frame);
} catch (BreakException e) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().loadError("break from proc-closure", this));
throw new RaiseException(getContext().getCoreLibrary().localJumpError("break from proc-closure", this));
}
}

Original file line number Diff line number Diff line change
@@ -791,11 +791,16 @@ public RubyException fiberError(String message, Node currentNode) {
return new RubyException(fiberErrorClass, context.makeString(message), RubyCallStack.getBacktrace(currentNode));
}

public RubyException fiberErrorDeadFiberCalled(Node currentNode) {
public RubyException deadFiberCalledError(Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return fiberError("dead fiber called", currentNode);
}

public RubyException yieldFromRootFiberError(Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return fiberError("can't yield from root fiber", currentNode);
}

public RubyContext getContext() {
return context;
}
76 changes: 59 additions & 17 deletions truffle/src/main/java/org/jruby/truffle/runtime/core/RubyFiber.java
Original file line number Diff line number Diff line change
@@ -14,6 +14,9 @@
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.objects.Allocator;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.BreakException;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.control.ReturnException;
import org.jruby.truffle.runtime.subsystems.FiberManager;
import org.jruby.truffle.runtime.subsystems.ThreadManager;
import org.jruby.truffle.runtime.subsystems.ThreadManager.BlockingActionWithoutGlobalLock;
@@ -63,6 +66,26 @@ public Object getArg() {
private static class FiberExitMessage implements FiberMessage {
}

private static class FiberExceptionMessage implements FiberMessage {

public RubyThread thread;
public RubyException exception;

public FiberExceptionMessage(RubyThread thread, RubyException exception) {
this.thread = thread;
this.exception = exception;
}

public RubyThread getThread() {
return thread;
}

public RubyException getException() {
return exception;
}

}

public static class FiberExitException extends ControlFlowException {

private static final long serialVersionUID = 1522270454305076317L;
@@ -72,14 +95,16 @@ public static class FiberExitException extends ControlFlowException {
private final FiberManager fiberManager;
private final ThreadManager threadManager;

private boolean topLevel;
private BlockingQueue<FiberMessage> messageQueue = new ArrayBlockingQueue<>(1);
private RubyFiber lastResumedByFiber = null;
private boolean alive;
private boolean alive = true;

public RubyFiber(RubyClass rubyClass, FiberManager fiberManager, ThreadManager threadManager) {
public RubyFiber(RubyClass rubyClass, FiberManager fiberManager, ThreadManager threadManager, boolean topLevel) {
super(rubyClass);
this.fiberManager = fiberManager;
this.threadManager = threadManager;
this.topLevel = topLevel;
}

public void initialize(RubyProc block) {
@@ -88,12 +113,10 @@ public void initialize(RubyProc block) {
final RubyFiber finalFiber = this;
final RubyProc finalBlock = block;

new Thread(new Runnable() {
final Thread thread = new Thread(new Runnable() {

@Override
public void run() {
alive = true;

finalFiber.getContext().getSafepointManager().enterThread();
fiberManager.registerFiber(finalFiber);

@@ -103,14 +126,22 @@ public void run() {
finalFiber.lastResumedByFiber.resume(finalFiber, result);
} catch (FiberExitException e) {
// Naturally exit the thread on catching this
} catch (ReturnException e) {
final RubyThread runningThread = threadManager.leaveGlobalLock();
finalFiber.lastResumedByFiber.messageQueue.add(new FiberExceptionMessage(runningThread, finalFiber.getContext().getCoreLibrary().unexpectedReturn(null)));
} catch (RaiseException e) {
final RubyThread runningThread = threadManager.leaveGlobalLock();
finalFiber.lastResumedByFiber.messageQueue.add(new FiberExceptionMessage(runningThread, e.getRubyException()));
} finally {
alive = false;
fiberManager.unregisterFiber(finalFiber);
finalFiber.getContext().getSafepointManager().leaveThread();
}
}

}).start();
});
thread.setName("Ruby Fiber@" + block.getSharedMethodInfo().getSourceSection().getShortDescription());
thread.start();
}

/**
@@ -128,18 +159,21 @@ public FiberMessage block() throws InterruptedException {
}
});

fiberManager.setCurrentFiber(this);

if (message instanceof FiberExitMessage) {
// TODO CS 2-Feb-15 what do we do about entering the global lock here?
throw new FiberExitException();
} else if (message instanceof FiberExceptionMessage) {
threadManager.enterGlobalLock(((FiberExceptionMessage) message).getThread());
throw new RaiseException(((FiberExceptionMessage) message).getException());
} else if (message instanceof FiberResumeMessage) {
threadManager.enterGlobalLock(((FiberResumeMessage) message).getThread());
lastResumedByFiber = ((FiberResumeMessage) message).getSendingFiber();
return ((FiberResumeMessage) message).getArg();
} else {
throw new UnsupportedOperationException();
}

final FiberResumeMessage resumeMessage = (FiberResumeMessage) message;

threadManager.enterGlobalLock(resumeMessage.getThread());

fiberManager.setCurrentFiber(this);

lastResumedByFiber = resumeMessage.getSendingFiber();
return resumeMessage.getArg();
}

/**
@@ -150,6 +184,8 @@ public FiberMessage block() throws InterruptedException {
public void resume(RubyFiber sendingFiber, Object... args) {
RubyNode.notDesignedForCompilation();

// TODO CS 2-Feb-15 move this logic into the node where we can specialise?

Object arg;

if (args.length == 0) {
@@ -172,18 +208,24 @@ public void shutdown() {
}

public boolean isAlive() {
return alive;
// TODO CS 2-Feb-15 race conditions (but everything in JRuby+Truffle is currently a race condition)
// TODO CS 2-Feb-15 should just be alive?
return alive || !messageQueue.isEmpty();
}

public RubyFiber getLastResumedByFiber() {
return lastResumedByFiber;
}

public boolean isTopLevel() {
return topLevel;
}

public static class FiberAllocator implements Allocator {

@Override
public RubyBasicObject allocate(RubyContext context, RubyClass rubyClass, RubyNode currentNode) {
return new RubyFiber(rubyClass, context.getFiberManager(), context.getThreadManager());
return new RubyFiber(rubyClass, context.getFiberManager(), context.getThreadManager(), false);
}

}
Original file line number Diff line number Diff line change
@@ -60,10 +60,10 @@ public void run() {
value = finalBlock.rootCall();
}

});
}, block.getSharedMethodInfo().getSourceSection().getShortDescription());
}

public void initialize(final RubyContext context, final RubyNode currentNode, Runnable runnable) {
public void initialize(final RubyContext context, final RubyNode currentNode, Runnable runnable, String name) {
final RubyThread finalThread = this;
final Runnable finalRunnable = runnable;

@@ -95,6 +95,7 @@ public void run() {
}

});
thread.setName("Ruby Thread@" + name);
thread.setDaemon(true);
thread.start();
}
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ public class FiberManager {
private final Set<RubyFiber> runningFibers = Collections.newSetFromMap(new ConcurrentHashMap<RubyFiber, Boolean>());

public FiberManager(RubyContext context) {
rootFiber = new RubyFiber(context.getCoreLibrary().getFiberClass(), this, context.getThreadManager());
rootFiber = new RubyFiber(context.getCoreLibrary().getFiberClass(), this, context.getThreadManager(), true);
currentFiber = rootFiber;
}

Original file line number Diff line number Diff line change
@@ -100,7 +100,7 @@ public void run() {
runFinalizers();
}

});
}, "finalizer");
}
}

Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@ public void poll() {
poll(true);
}

private void poll(boolean holdsGlobalLock) {
public void poll(boolean holdsGlobalLock) {
try {
assumption.check();
} catch (InvalidAssumptionException e) {
Original file line number Diff line number Diff line change
@@ -24,6 +24,8 @@
*/
public class ThreadManager {

private final RubyContext context;

private final ReentrantLock globalLock = new ReentrantLock();

private final RubyThread rootThread;
@@ -32,6 +34,7 @@ public class ThreadManager {
private final Set<RubyThread> runningRubyThreads = Collections.newSetFromMap(new ConcurrentHashMap<RubyThread, Boolean>());

public ThreadManager(RubyContext context) {
this.context = context;
rootThread = new RubyThread(context.getCoreLibrary().getThreadClass(), this);
rootThread.setRootThread(Thread.currentThread());
runningRubyThreads.add(rootThread);
@@ -131,7 +134,7 @@ public <T> T runOnce(boolean holdsGIL, BlockingActionWithoutGlobalLock<T> action
}
} catch (InterruptedException e) {
// We were interrupted, possibly by the SafepointManager.
runningThread.getContext().getSafepointManager().poll();
context.getSafepointManager().poll(holdsGIL);
}
return result;
}