Skip to content

Commit

Permalink
Showing 12 changed files with 879 additions and 691 deletions.
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ public InitializeNode(InitializeNode prev) {
public RubyNilClass initialize(RubyThread thread, RubyProc block) {
notDesignedForCompilation();

thread.initialize(this, block);
thread.initialize(getContext(), this, block);
return getContext().getCoreLibrary().getNilObject();
}

658 changes: 658 additions & 0 deletions core/src/main/java/org/jruby/truffle/nodes/debug/ProxyNode.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@
import com.oracle.truffle.api.instrument.ASTProber;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.util.cli.Options;

import java.util.ArrayList;
import java.util.List;
@@ -28,7 +27,7 @@ public RubyASTProber() {
}

if (RubyContext.OBJECTSPACE) {
probers.add(new ObjectSpaceSafepointProber());
probers.add(new SafepointProber());
}
}

607 changes: 18 additions & 589 deletions core/src/main/java/org/jruby/truffle/nodes/debug/RubyWrapper.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -14,17 +14,17 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.runtime.RubyContext;

public class ObjectSpaceSafepointInstrument extends Instrument {
public class SafepointInstrument extends Instrument {

private final RubyContext context;

public ObjectSpaceSafepointInstrument(RubyContext context) {
public SafepointInstrument(RubyContext context) {
this.context = context;
}

@Override
public void enter(Node node, VirtualFrame frame) {
context.getObjectSpaceManager().checkSafepoint();
context.getSafepointManager().poll();
}

}
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
import com.oracle.truffle.api.nodes.Node;
import org.jruby.truffle.nodes.RubyNode;

public class ObjectSpaceSafepointProber implements RubyNodeProber {
public class SafepointProber implements RubyNodeProber {

@Override
public RubyNode probeAsStatement(RubyNode node) {
@@ -32,7 +32,7 @@ public RubyNode probeAsPeriodic(RubyNode node) {
}

wrapper.tagAs(StandardSyntaxTag.PERIODIC);
wrapper.getProbe().addInstrument(new ObjectSpaceSafepointInstrument(node.getContext()));
wrapper.getProbe().addInstrument(new SafepointInstrument(node.getContext()));

return wrapper;
}
8 changes: 6 additions & 2 deletions core/src/main/java/org/jruby/truffle/runtime/RubyContext.java
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@

import java.io.*;
import java.math.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayDeque;
@@ -25,7 +24,6 @@
import com.oracle.truffle.api.*;
import com.oracle.truffle.api.source.*;
import com.oracle.truffle.api.frame.*;
import com.oracle.truffle.api.nodes.*;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.truffle.TruffleHooks;
import org.jruby.truffle.nodes.RubyNode;
@@ -75,6 +73,7 @@ public class RubyContext extends ExecutionContext {
private final AtExitManager atExitManager;
private final RubySymbol.SymbolTable symbolTable = new RubySymbol.SymbolTable(this);
private final Warnings warnings;
private final SafepointManager safepointManager;

private SourceCallback sourceCallback = null;

@@ -92,6 +91,8 @@ protected Queue<Object> initialValue() {
public RubyContext(Ruby runtime) {
assert runtime != null;

safepointManager = new SafepointManager(this);

this.runtime = runtime;
translator = new TranslatorDriver(this);
astProber = new RubyASTProber();
@@ -408,4 +409,7 @@ public Queue<Object> getThrowTags() {
return throwTags.get();
}

public SafepointManager getSafepointManager() {
return safepointManager;
}
}
Original file line number Diff line number Diff line change
@@ -13,10 +13,10 @@
import java.util.concurrent.*;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.control.ReturnException;
import org.jruby.truffle.runtime.subsystems.*;
import org.jruby.truffle.runtime.util.Supplier;

/**
* Represents the Ruby {@code Thread} class. Implemented using Java threads, but note that there is
@@ -55,10 +55,10 @@ public RubyThread(RubyClass rubyClass, ThreadManager manager) {
this.manager = manager;
}

public void initialize(final RubyNode currentNode, RubyProc block) {
public void initialize(RubyContext context, RubyNode currentNode, RubyProc block) {
final RubyProc finalBlock = block;

initialize(currentNode, new Runnable() {
initialize(context, currentNode, new Runnable() {

@Override
public void run() {
@@ -68,7 +68,7 @@ public void run() {
});
}

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

@@ -78,6 +78,7 @@ public void initialize(final RubyNode currentNode, Runnable runnable) {
public void run() {
finalThread.manager.registerThread(finalThread);
finalThread.manager.enterGlobalLock(finalThread);
context.getSafepointManager().enterThread();

try {
finalRunnable.run();
@@ -89,6 +90,7 @@ public void run() {
finalThread.manager.leaveGlobalLock();
finalThread.manager.unregisterThread(finalThread);
finalThread.finished.countDown();
context.getSafepointManager().leaveThread();
}
}

Original file line number Diff line number Diff line change
@@ -13,17 +13,15 @@
import java.util.*;
import java.util.concurrent.*;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.FrameInstanceVisitor;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.core.*;
import org.jruby.truffle.runtime.util.Consumer;

/**
* Supports the Ruby {@code ObjectSpace} module. Object IDs are lazily allocated {@code long}
@@ -94,7 +92,7 @@ public void defineFinalizer(RubyBasicObject object, RubyProc proc) {

finalizerThread = new RubyThread(context.getCoreLibrary().getThreadClass(), context.getThreadManager());

finalizerThread.initialize(null, new Runnable() {
finalizerThread.initialize(context, null, new Runnable() {

@Override
public void run() {
@@ -207,101 +205,37 @@ public static interface ObjectGraphVisitor {
private Map<Long, RubyBasicObject> liveObjects;
private ObjectGraphVisitor visitor;

@CompilerDirectives.CompilationFinal private Assumption notStoppingAssumption = Truffle.getRuntime().createAssumption();
private CyclicBarrier stoppedBarrier;
private CyclicBarrier markedBarrier;

public void checkSafepoint() {
try {
notStoppingAssumption.check();
} catch (InvalidAssumptionException e) {
context.outsideGlobalLock(new Runnable() {

@Override
public void run() {
while (true) {
try {
stoppedBarrier.await();
break;
} catch (InterruptedException | BrokenBarrierException e2) {
}
}

synchronized (liveObjects) {
visitCallStack(visitor);
}

while (true) {
try {
markedBarrier.await();
break;
} catch (InterruptedException | BrokenBarrierException e2) {
}
}

// TODO(CS): error recovery
}

});
}
}

public Map<Long, RubyBasicObject> collectLiveObjects() {
RubyNode.notDesignedForCompilation();

synchronized (context.getThreadManager()) {
context.outsideGlobalLock(new Runnable() {
// TODO(CS): probably a race condition here if multiple threads try to collect at the same time

@Override
public void run() {
liveObjects = new HashMap<>();
liveObjects = new HashMap<>();

visitor = new ObjectGraphVisitor() {
visitor = new ObjectGraphVisitor() {

@Override
public boolean visit(RubyBasicObject object) {
return liveObjects.put(object.getObjectID(), object) == null;
}

};

stoppedBarrier = new CyclicBarrier(2);
markedBarrier = new CyclicBarrier(2);

notStoppingAssumption.invalidate();

while (true) {
try {
stoppedBarrier.await();
break;
} catch (InterruptedException | BrokenBarrierException e) {
}
}

synchronized (liveObjects) {
context.getCoreLibrary().getGlobalVariablesObject().visitObjectGraph(visitor);
context.getCoreLibrary().getMainObject().visitObjectGraph(visitor);
context.getCoreLibrary().getObjectClass().visitObjectGraph(visitor);
visitCallStack(visitor);
}
@Override
public boolean visit(RubyBasicObject object) {
return liveObjects.put(object.getObjectID(), object) == null;
}

notStoppingAssumption = Truffle.getRuntime().createAssumption();
};

while (true) {
try {
markedBarrier.await();
break;
} catch (InterruptedException | BrokenBarrierException e) {
}
}
context.getSafepointManager().pauseAllThreadsAndExecute(new Consumer<Boolean>() {

// TODO(CS): error recovery
@Override
public void accept(Boolean pausingThread) {
synchronized (liveObjects) {
context.getCoreLibrary().getGlobalVariablesObject().visitObjectGraph(visitor);
context.getCoreLibrary().getMainObject().visitObjectGraph(visitor);
context.getCoreLibrary().getObjectClass().visitObjectGraph(visitor);
visitCallStack(visitor);
}
}

});
});

return Collections.unmodifiableMap(liveObjects);
}
return Collections.unmodifiableMap(liveObjects);
}

public void visitCallStack(final ObjectGraphVisitor visitor) {
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.runtime.subsystems;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerAsserts;
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.util.Consumer;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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;

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

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

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

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

try {
lock.lock();

poll();

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

public void poll() {
try {
assumption.check();
} catch (InvalidAssumptionException e) {
context.outsideGlobalLock(new Runnable() {

@Override
public void run() {
while (true) {
try {
barrier.await();
break;
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
}
}

action.accept(false);

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

}

});
}
}

public void pauseAllThreadsAndExecute(final Consumer<Boolean> action) {
CompilerAsserts.neverPartOfCompilation();

context.outsideGlobalLock(new Runnable() {

@Override
public void run() {
try {
lock.lock();

SafepointManager.this.action = action;

barrier = new CyclicBarrier(liveThreads);

assumption.invalidate();

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

action.accept(true);

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

assumption = Truffle.getRuntime().createAssumption();
} finally {
lock.unlock();
}
}

});
}

}
16 changes: 16 additions & 0 deletions core/src/main/java/org/jruby/truffle/runtime/util/Consumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.runtime.util;

public interface Consumer<T> {

void accept(T value);

}
Original file line number Diff line number Diff line change
@@ -20,7 +20,6 @@
import org.jruby.truffle.nodes.control.*;
import org.jruby.truffle.nodes.control.AndNode;
import org.jruby.truffle.nodes.control.IfNode;
import org.jruby.truffle.nodes.debug.ObjectSpaceSafepointInstrument;
import org.jruby.truffle.nodes.literal.ObjectLiteralNode;
import org.jruby.truffle.nodes.methods.*;
import org.jruby.truffle.nodes.methods.arguments.*;

3 comments on commit 9951841

@mkristian
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reverted that commit since jruby/core does not compile anymore and it is not possible to very my fix for mvn -Posgi on travis if nothing compiles. hope that is OK for you - I looked like there were just classes missing on the classpath.

@chrisseaton
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks. I should build from clean before I commit - sometimes Maven doesn't seem to get the recompilation of generated sources quite right.

@chrisseaton
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem was a file renamed by changing the case of one letter - turns out this upsets either OS X or Git, not sure which.

Please sign in to comment.