diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java index b5b3b0cf66c61195d61d16a118b4ad71273b0d06..83a6bc602be0e0d9f598fb7280b999b2a706ca63 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java @@ -80,6 +80,27 @@ public class FastRContext { } } + public abstract static class Join extends RExternalBuiltinNode.Arg1 { + @Specialization + protected RNull eval(RIntVector contexts) { + try { + for (int i = 0; i < contexts.getLength(); i++) { + RContext context = RContext.find(contexts.getDataAt(i)); + if (context == null) { + // already done + continue; + } else { + context.joinThread(); + } + } + } catch (InterruptedException ex) { + throw RError.error(this, RError.Message.GENERIC, "error finishing eval thread"); + + } + return RNull.instance; + } + } + public abstract static class Eval extends RExternalBuiltinNode.Arg3 { @Specialization protected RNull eval(RIntVector contexts, RAbstractStringVector exprs, byte par) { @@ -97,7 +118,7 @@ public class FastRContext { threads[i].join(); } } catch (InterruptedException ex) { - + throw RError.error(this, RError.Message.GENERIC, "error finishing eval thread"); } } else { for (int i = 0; i < contexts.getLength(); i++) { diff --git a/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE b/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE index 279c0d1878a7940fa797829088074a72526280c0..ab0090b5af4eab4bbc16cd707eb132c49d9c865c 100644 --- a/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE +++ b/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE @@ -13,6 +13,7 @@ export(fastr.createpkgsources) export(fastr.createpkgsource) export(fastr.context.create) export(fastr.context.spawn) +export(fastr.context.join) export(fastr.context.eval) export(fastr.context.pareval) export(print.fastr_context) diff --git a/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R b/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R index bea405893fcf6e71964584f2296d6c5050843d04..d782c83b104bdb686dec7a1dffce65a3f161c9c7 100644 --- a/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R +++ b/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R @@ -90,6 +90,11 @@ fastr.context.spawn <- function(contexts, exprs) { invisible(NULL) } +fastr.context.join <- function(contexts) { + .FastR(.NAME="context.join", contexts) + invisible(NULL) +} + fastr.context.eval <- function(contexts, exprs, par=FALSE) { .FastR(.NAME="context.eval", contexts, exprs, par) invisible(NULL) diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java index 02044b58461a11fd0dd4d55d14aff799f78b1e6e..c8f9b77579392aa2d219c2afc57a6cad0b905116 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java @@ -88,6 +88,8 @@ public abstract class FastR extends RBuiltinNode { return FastRContextFactory.PrintNodeGen.create(); case "context.spawn": return FastRContextFactory.SpawnNodeGen.create(); + case "context.join": + return FastRContextFactory.JoinNodeGen.create(); case "context.eval": return FastRContextFactory.EvalNodeGen.create(); case "fastr.channel.create": diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConvertBooleanNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConvertBooleanNode.java index fb936c694c4c9cc7e7b23eead1f6bd54ed44df51..7424142247365787ca920c61828e47e157bc9a34 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConvertBooleanNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConvertBooleanNode.java @@ -152,6 +152,12 @@ public abstract class ConvertBooleanNode extends RNode { return doRaw(value.getDataAt(0)); } + @Specialization + protected byte doRawVector(RList value) { + checkLength(value); + throw RError.error(this, RError.Message.ARGUMENT_NOT_INTERPRETABLE_LOGICAL); + } + public static ConvertBooleanNode create(RSyntaxNode node) { if (node instanceof ConvertBooleanNode) { return (ConvertBooleanNode) node; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java index 1fd01083ea3fb5d460f6013aca4a6302437d2698..dad360bda10a469e49011b1084c2a3db844b0556 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java @@ -22,9 +22,11 @@ */ package com.oracle.truffle.r.runtime; +import java.io.*; import java.util.concurrent.*; import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; /** * Implementation of a channel abstraction used for communication between parallel contexts in @@ -32,6 +34,8 @@ import com.oracle.truffle.r.runtime.data.*; */ public class RChannel { + // TODO: cheaper way of serializing data (re-usable buffer?) + private static final int INITIAL_CHANNEL_NUM = 4; private static final int CHANNEL_NUM_GROW_FACTOR = 2; private static final int QUEUE_CAPACITY = 1; @@ -135,19 +139,23 @@ public class RChannel { } public static void send(int id, Object data) { + Object msg = data; RChannel channel = getChannelFromId(id); - if (data instanceof RShareable) { - // make sure that what's passed through the channel will be copied on the first update - RShareable shareable = (RShareable) data; + if ((msg instanceof RAbstractVector && !(msg instanceof RList)) || msg instanceof RDataFrame || msg instanceof RFactor) { + // make sure that what's passed through the channel will be copied on the first + // update + RShareable shareable = (RShareable) msg; if (FastROptions.NewStateTransition) { shareable.incRefCount(); shareable.incRefCount(); } else { shareable.makeShared(); } + } else { + msg = RSerialize.serialize(msg, false, true, RSerialize.DEFAULT_VERSION, null); } try { - (id > 0 ? channel.masterToClient : channel.clientToMaster).put(data); + (id > 0 ? channel.masterToClient : channel.clientToMaster).put(msg); } catch (InterruptedException x) { throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error sending through the channel"); } @@ -156,10 +164,16 @@ public class RChannel { public static Object receive(int id) { RChannel channel = getChannelFromId(id); try { - return (id < 0 ? channel.masterToClient : channel.clientToMaster).take(); + Object msg = (id < 0 ? channel.masterToClient : channel.clientToMaster).take(); + if (msg instanceof byte[]) { + return RSerialize.unserialize((byte[]) msg, null, null); + } else { + return msg; + } } catch (InterruptedException x) { throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error receiving from the channel"); + } catch (IOException x) { + throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error unserializing msg from the channel"); } } - } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java index df17bf7938633c8033f66e801f70d76334f3495f..0f5618d8337bbeef1b63bb0c684ee4c5f88a9ef3 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java @@ -448,6 +448,7 @@ public final class RContext extends ExecutionContext { public EvalThread(RContext context, Source source) { super(context); this.source = source; + context.evalThread = this; } @Override @@ -509,6 +510,11 @@ public final class RContext extends ExecutionContext { */ private RContext sharedChild; + /** + * Back pointer to the evalThread. + */ + private EvalThread evalThread; + /** * Typically there is a 1-1 relationship between an {@link RContext} and the thread that is * performing the evaluation, so we can store the {@link RContext} in a {@link ThreadLocal}. @@ -535,6 +541,8 @@ public final class RContext extends ExecutionContext { private static final Deque<RContext> allContexts = new ConcurrentLinkedDeque<>(); + private static final Semaphore allContextsSemaphore = new Semaphore(1, true); + /** * A (hopefully) temporary workaround to ignore the setting of {@link #resultVisible} for * benchmarks. Set across all contexts. @@ -568,6 +576,20 @@ public final class RContext extends ExecutionContext { } } + /** + * Waits for the associated EvalThread to finish. + * + * @throws InterruptedException + */ + public void joinThread() throws InterruptedException { + EvalThread t = this.evalThread; + if (t == null) { + throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "no eval thread in a given context"); + } + this.evalThread = null; + t.join(); + } + private static final Assumption singleContextAssumption = Truffle.getRuntime().createAssumption("single RContext"); @CompilationFinal private static RContext singleContext; @@ -587,7 +609,13 @@ public final class RContext extends ExecutionContext { } this.consoleHandler = consoleHandler; this.interactive = consoleHandler.isInteractive(); - allContexts.add(this); + try { + allContextsSemaphore.acquire(); + allContexts.add(this); + allContextsSemaphore.release(); + } catch (InterruptedException x) { + throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error destroying context"); + } if (singleContextAssumption.isValid()) { if (singleContext == null) { @@ -700,7 +728,13 @@ public final class RContext extends ExecutionContext { parent.sharedChild = null; } engine = null; - allContexts.remove(this); + try { + allContextsSemaphore.acquire(); + allContexts.remove(this); + allContextsSemaphore.release(); + } catch (InterruptedException x) { + throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error destroying context"); + } if (parent == null) { threadLocalContext.set(null); } else { @@ -721,10 +755,17 @@ public final class RContext extends ExecutionContext { } public static RContext find(int id) { - for (RContext context : allContexts) { - if (context.id == id) { - return context; + try { + allContextsSemaphore.acquire(); + for (RContext context : allContexts) { + if (context.id == id) { + allContextsSemaphore.release(); + return context; + } } + allContextsSemaphore.release(); + } catch (InterruptedException x) { + throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error destroying context"); } return null; } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 166d4a7d4b24a34f68a14d119f6b7181f4714f8e..2e009360b4ed9a06bc1c584adc83b7fe1e152369 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -49442,6 +49442,10 @@ $x #{ x<-7; f<-function() x<<-42; f_copy<-as.list(environment())[["f"]]; f_copy(); x } [1] 42 +##com.oracle.truffle.r.test.builtins.TestMiscBuiltins.testChannels +#{ if (length(grep("FastR", R.Version()$version.string)) == 1) { ch <- fastr.channel.create(1L); cx <- fastr.context.create("SHARED_NOTHING"); fastr.context.spawn(cx, "ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1]<-7; fastr.channel.send(ch, x)"); y<-c(42); fastr.channel.send(ch, y); x<-fastr.channel.receive(ch); fastr.context.join(cx); fastr.channel.close(ch); print(c(x,y)) } else { print(c(7L, 42L)) } } +[1] 7 42 + ##com.oracle.truffle.r.test.builtins.TestMiscBuiltins.testDataFrameTypeCheck #is.data.frame("1") [1] FALSE @@ -57808,6 +57812,13 @@ Error in if (TRUE == NA) TRUE else FALSE : #{ x <- 2 ; if (NA) x <- 3 ; x } Error in if (NA) x <- 3 : missing value where TRUE/FALSE needed +##com.oracle.truffle.r.test.library.base.TestSimpleIfEvaluator.testIf +#{ x<-list(1,2); if (x) 7 else 42 } +Error in if (x) 7 else 42 : argument is not interpretable as logical +In addition: Warning message: +In if (x) 7 else 42 : + the condition has length > 1 and only the first element will be used + ##com.oracle.truffle.r.test.library.base.TestSimpleIfEvaluator.testIf2 #if(FALSE) 1 else 2 [1] 2 diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java index 8e5ad03a215cc0f8728fca9d6f3f66d42797e878..de5c5c41adb057874d1c8bed7d057b6734321ca7 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java @@ -14,6 +14,7 @@ import org.junit.*; import com.oracle.truffle.r.test.*; +// Checkstyle: stop line length check public class TestMiscBuiltins extends TestBase { @Test @@ -344,4 +345,9 @@ public class TestMiscBuiltins extends TestBase { public void testTypeConvert() { assertEval("{ x<-as.character(list(a=\"0\", b=\"0\", c=\"0.3\")); type.convert(x, as.is=FALSE) }"); } + + @Test + public void testChannels() { + assertEval("{ if (length(grep(\"FastR\", R.Version()$version.string)) == 1) { ch <- fastr.channel.create(1L); cx <- fastr.context.create(\"SHARED_NOTHING\"); fastr.context.spawn(cx, \"ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1]<-7; fastr.channel.send(ch, x)\"); y<-c(42); fastr.channel.send(ch, y); x<-fastr.channel.receive(ch); fastr.context.join(cx); fastr.channel.close(ch); print(c(x,y)) } else { print(c(7L, 42L)) } }"); + } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleIfEvaluator.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleIfEvaluator.java index 1d3ec43d1390f07621d1d9a07025303dc267226d..bd1116b82d262f37edbb81b65e712aac4c8438ec 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleIfEvaluator.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleIfEvaluator.java @@ -67,6 +67,7 @@ public class TestSimpleIfEvaluator extends TestBase { assertEval("{ if (FALSE==TRUE) TRUE else FALSE }"); assertEval("{ if (FALSE==1) TRUE else FALSE }"); assertEval("{ f <- function(v) { if (FALSE==v) TRUE else FALSE } ; f(TRUE) ; f(1) }"); + assertEval(Output.ContainsError, Output.ContainsWarning, "{ x<-list(1,2); if (x) 7 else 42 }"); } @Test diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py index 15e033ce3c6d94ccf5aec45575d09fa3a0e06587..67ae1eb18005cb571ea07d859e3892022c8b9a0e 100644 --- a/mx.fastr/mx_fastr.py +++ b/mx.fastr/mx_fastr.py @@ -157,8 +157,8 @@ def _test_harness_body(args, vmArgs): print "placeholder for mx test" def test(args): - vm = mx_graal.VM('server' if mx_graal._vm is None else mx_graal._vm) - with vm: + vm = _get_graal_vm() + with mx_jvmci.VM(vm): mx.test(args, harness=_test_harness_body) def _test_srcdir(): diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py index 3f060b89d42e33cb649002b0f90f5c9c9123dc7b..c31aea79bd0904f4ba24fad93b5af209fb4a8c13 100644 --- a/mx.fastr/suite.py +++ b/mx.fastr/suite.py @@ -21,14 +21,14 @@ # questions. # suite = { - "mxversion" : "5.2.1", + "mxversion" : "5.4.1", "name" : "fastr", "imports" : { "suites" : [ { "name" : "graal", - "version" : "4369b936c52001d16254ded0211134f3020ba78c", + "version" : "21baeafdf6a53ff96fedc6d98c2db398f3d92d0a", "urls" : [{"url" : "http://lafo.ssw.uni-linz.ac.at/hg/graal-compiler", "kind" : "hg"}] }, ],