Skip to content
Snippets Groups Projects
Commit e598e2f1 authored by Mick Jordan's avatar Mick Jordan
Browse files

Merge pull request #518 in G/fastr from ~MICK.JORDAN_ORACLE.COM/fastr:feature/timebox to master

* commit 'bdef5e6d':
  Use Truffle TimeBoxing to timeout unit tests
  update Truffle version
parents 36272ac0 bdef5e6d
No related branches found
No related tags found
No related merge requests found
...@@ -26,11 +26,12 @@ import java.util.ArrayDeque; ...@@ -26,11 +26,12 @@ import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Deque; import java.util.Deque;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.Semaphore; import java.util.Timer;
import java.util.concurrent.TimeUnit; import java.util.TimerTask;
import java.util.concurrent.TimeoutException;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.debug.Debugger;
import com.oracle.truffle.api.debug.SuspendedCallback;
import com.oracle.truffle.api.debug.SuspendedEvent;
import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.api.vm.PolyglotEngine;
import com.oracle.truffle.r.runtime.RCmdOptions; import com.oracle.truffle.r.runtime.RCmdOptions;
...@@ -133,8 +134,6 @@ public final class FastRSession implements RSession { ...@@ -133,8 +134,6 @@ public final class FastRSession implements RSession {
private final PolyglotEngine main; private final PolyglotEngine main;
private final RContext mainContext; private final RContext mainContext;
private EvalThread evalThread;
public static FastRSession create() { public static FastRSession create() {
if (singleton == null) { if (singleton == null) {
singleton = new FastRSession(); singleton = new FastRSession();
...@@ -191,143 +190,81 @@ public final class FastRSession implements RSession { ...@@ -191,143 +190,81 @@ public final class FastRSession implements RSession {
* {@link #eval} but also used for package installation via the {@code system2} command, where * {@link #eval} but also used for package installation via the {@code system2} command, where
* the result is used to check whether the installation succeeded. * the result is used to check whether the installation succeeded.
*/ */
@SuppressWarnings("deprecation")
public Object evalAsObject(TestBase testClass, String expression, ContextInfo contextInfo, boolean longTimeout) throws Throwable { public Object evalAsObject(TestBase testClass, String expression, ContextInfo contextInfo, boolean longTimeout) throws Throwable {
Object result = null;
Timer timer = null;
consoleHandler.reset(); consoleHandler.reset();
EvalThread thread = evalThread;
if (thread == null || !thread.isAlive() || contextInfo != thread.contextInfo) {
thread = new EvalThread(contextInfo);
thread.setName("FastR evaluation");
thread.start();
evalThread = thread;
}
thread.push(testClass, expression);
try { try {
if (!thread.await(longTimeout ? longTimeoutValue : timeoutValue)) { ContextInfo actualContextInfo = checkContext(contextInfo);
consoleHandler.println("<timeout>"); // set up some interop objects used by fastr-specific tests:
System.out.println("timeout in " + testClass.getClass() + ": " + expression); PolyglotEngine.Builder builder = PolyglotEngine.newBuilder();
for (StackTraceElement element : thread.getStackTrace()) { if (testClass != null) {
System.out.println(element); testClass.addPolyglotSymbols(builder);
}
thread.stop();
evalThread.ensureContextDestroyed();
evalThread = null;
throw new TimeoutException();
} }
} catch (InterruptedException e1) { PolyglotEngine vm = actualContextInfo.createVM(builder);
e1.printStackTrace(); timer = scheduleTimeBoxing(vm, longTimeout ? longTimeoutValue : timeoutValue);
} consoleHandler.setInput(expression.split("\n"));
if (thread.killedByException != null) { try {
evalThread = null; String input = consoleHandler.readLine();
throw thread.killedByException; while (input != null) {
} Source source = RSource.fromTextInternal(input, RSource.Internal.UNIT_TEST);
return evalThread.result;
}
private final class EvalThread extends RContext.ContextThread {
private volatile String expression;
private volatile Throwable killedByException;
private final Semaphore entry = new Semaphore(0);
private final Semaphore exit = new Semaphore(0);
private final ContextInfo contextInfo;
private TestBase testClass;
private Object result;
/**
* Create an evaluation thread (to handle timeouts).
*
* @param contextInfo {@code null} for a lightweight test context, else an existing one to
* use.
*/
EvalThread(ContextInfo contextInfo) {
super(null);
this.contextInfo = contextInfo;
setDaemon(true);
}
public void push(TestBase testClassArg, String exp) {
this.expression = exp;
this.testClass = testClassArg;
this.entry.release();
}
public boolean await(int millisTimeout) throws InterruptedException {
return exit.tryAcquire(millisTimeout, TimeUnit.MILLISECONDS);
}
/**
* In case the vm is not disposed by the {@code finally} clause in run after a timeout,
* (which should not happen), we explicitly destroy the context, to avoid subsequent errors
* relating to multiple children of a single SHARED_RW context.
*/
public void ensureContextDestroyed() {
context.destroy();
}
@Override
public void run() {
while (killedByException == null) {
try {
entry.acquire();
} catch (InterruptedException e) {
break;
}
try {
ContextInfo actualContextInfo = checkContext(contextInfo);
// set up some interop objects used by fastr-specific tests:
PolyglotEngine.Builder builder = PolyglotEngine.newBuilder();
if (testClass != null) {
testClass.addPolyglotSymbols(builder);
}
PolyglotEngine vm = actualContextInfo.createVM(builder);
consoleHandler.setInput(expression.split("\n"));
try { try {
String input = consoleHandler.readLine(); try {
while (input != null) { result = vm.eval(source).get();
Source source = RSource.fromTextInternal(input, RSource.Internal.UNIT_TEST); // checked exceptions are wrapped in RuntimeExceptions
try { } catch (RuntimeException e) {
try { if (e.getCause() instanceof com.oracle.truffle.api.vm.IncompleteSourceException) {
result = vm.eval(source).get(); throw e.getCause().getCause();
// checked exceptions are wrapped in RuntimeExceptions } else {
} catch (RuntimeException e) { throw e;
if (e.getCause() instanceof com.oracle.truffle.api.vm.IncompleteSourceException) {
throw e.getCause().getCause();
} else {
throw e;
}
}
input = consoleHandler.readLine();
} catch (IncompleteSourceException e) {
String additionalInput = consoleHandler.readLine();
if (additionalInput == null) {
throw e;
}
input += "\n" + additionalInput;
} }
} }
} finally { input = consoleHandler.readLine();
vm.dispose(); } catch (IncompleteSourceException e) {
} String additionalInput = consoleHandler.readLine();
} catch (ParseException e) { if (additionalInput == null) {
e.report(consoleHandler); throw e;
} catch (RError e) {
// nothing to do
} catch (Throwable t) {
if (!TestBase.ProcessFailedTests) {
if (t instanceof RInternalError) {
RInternalError.reportError(t);
} }
t.printStackTrace(); input += "\n" + additionalInput;
} }
killedByException = t;
} finally {
exit.release();
} }
} finally {
vm.dispose();
}
} catch (ParseException e) {
e.report(consoleHandler);
} catch (RError e) {
// nothing to do
} catch (Throwable t) {
if (!TestBase.ProcessFailedTests) {
if (t instanceof RInternalError) {
RInternalError.reportError(t);
}
t.printStackTrace();
}
throw t;
} finally {
if (timer != null) {
timer.cancel();
} }
} }
return result;
}
private static Timer scheduleTimeBoxing(PolyglotEngine engine, long timeout) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Debugger.find(engine).startSession(new SuspendedCallback() {
@Override
public void onSuspend(SuspendedEvent event) {
event.prepareKill();
}
}).suspendNextExecution();
}
}, timeout);
return timer;
} }
@Override @Override
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment