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"}]
             },
         ],