diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
index d05f42daf1e7ae23032d741b8de5ba534453d367..1fec8cd3b23c6b2290daafc56f8784015f74b0b5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
@@ -142,7 +142,7 @@ public class FastRContext {
             int[] multiSlotIndices = new int[length];
             for (int i = 0; i < length; i++) {
                 ChildContextInfo info = createContextInfo(contextKind);
-                threads[i] = new EvalThread(info, RSource.fromTextInternalInvisible(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL),
+                threads[i] = new EvalThread(RContext.getInstance().threads, info, RSource.fromTextInternalInvisible(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL),
                                 FastROptions.SpawnUsesPolyglot.getBooleanValue());
                 data[i] = info.getId();
                 multiSlotIndices[i] = info.getMultiSlotInd();
@@ -175,7 +175,7 @@ public class FastRContext {
                 int[] multiSlotIndices = new int[handle.getLength()];
                 for (int i = 0; i < handle.getLength(); i++) {
                     int id = handle.getDataAt(i);
-                    Thread thread = EvalThread.threads.get(id);
+                    Thread thread = RContext.getInstance().threads.get(id);
                     if (EvalThread.idToMultiSlotTable.containsKey(id)) {
                         multiSlotIndices[i] = EvalThread.idToMultiSlotTable.remove(id);
                     }
@@ -245,7 +245,7 @@ public class FastRContext {
                 int[] multiSlotIndices = new int[length];
                 for (int i = 0; i < length; i++) {
                     ChildContextInfo info = createContextInfo(contextKind);
-                    threads[i] = new EvalThread(info, RSource.fromTextInternalInvisible(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL), false);
+                    threads[i] = new EvalThread(RContext.getInstance().threads, info, RSource.fromTextInternalInvisible(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL), false);
                     multiSlotIndices[i] = info.getMultiSlotInd();
                 }
                 if (contextKind == ContextKind.SHARE_ALL) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
index ff122d9ae283925310bf45f50762ab6ca98be07c..d33648e88f25646b17a93d579f973f4e91c46b12 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
@@ -101,6 +101,16 @@ public class RCleanUp {
             }
         }
         // TODO run exit finalizers (FFI) (this should happen in the FFI context beforeDestroy)
+
+        // force sub-context threads to stop
+        for (Thread thread : new ArrayList<>(RContext.getInstance().threads.values())) {
+            thread.interrupt();
+            try {
+                thread.join(10);
+            } catch (InterruptedException e) {
+                // nothing to be done
+            }
+        }
         throw new ExitException(status, false);
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/EvalThread.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/EvalThread.java
index cfa1fe7b790ae4e82ce0b0aeb222a4e2081ae2d3..1f7f44cfd908b41d44562972e372863bb67212b5 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/EvalThread.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/EvalThread.java
@@ -53,7 +53,7 @@ public class EvalThread extends Thread {
     private RList evalResult;
     private Semaphore init = new Semaphore(0);
 
-    public static final Map<Integer, Thread> threads = new ConcurrentHashMap<>();
+    private final Map<Integer, Thread> threadMap;
 
     /** This table is required to create several bunches of child contexts. */
     public static final Map<Integer, Integer> idToMultiSlotTable = new ConcurrentHashMap<>();
@@ -61,11 +61,12 @@ public class EvalThread extends Thread {
     /** We use a separate counter for threads since ConcurrentHashMap.size() is not reliable. */
     public static final AtomicInteger threadCnt = new AtomicInteger(0);
 
-    public EvalThread(ChildContextInfo info, Source source, boolean usePolyglot) {
+    public EvalThread(Map<Integer, Thread> threadMap, ChildContextInfo info, Source source, boolean usePolyglot) {
+        this.threadMap = threadMap;
         this.info = info;
         this.source = source;
         threadCnt.incrementAndGet();
-        threads.put(info.getId(), this);
+        threadMap.put(info.getId(), this);
         idToMultiSlotTable.put(info.getId(), info.getMultiSlotInd());
         this.usePolyglot = usePolyglot;
         this.truffleContext = usePolyglot ? null : info.createTruffleContext();
@@ -82,7 +83,7 @@ public class EvalThread extends Thread {
                 evalResult = run(truffleContext, info, source);
             }
         } finally {
-            threads.remove(info.getId());
+            threadMap.remove(info.getId());
             threadCnt.decrementAndGet();
         }
     }
@@ -109,8 +110,9 @@ public class EvalThread extends Thread {
             parent = truffleContext.enter();
             // this is the engine for the new child context
             Engine rEngine = RContext.getEngine();
+            // Object eval = rEngine.eval(rEngine.parse(source), rEngine.getGlobalFrame());
             Object evalResult = rEngine.parseAndEval(source, rEngine.getGlobalFrame(), false);
-            result = createEvalResult(evalResult, false);
+            result = createEvalResult(evalResult == null ? RNull.instance : evalResult, false);
         } catch (ParseException e) {
             e.report(info.getStdout());
             result = createErrorResult(e.getMessage());
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
index 30f785da5a551edad998e8d3893b6146f433e711..e229eb4c45359b8f129193e759f89db870ad906b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
@@ -46,6 +46,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.TimeZone;
 import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 
@@ -353,6 +354,7 @@ public final class RContext implements RTruffleObject {
     public final WeakHashMap<Source, REnvironment> sourceRefEnvironments = new WeakHashMap<>();
     public final WeakHashMap<Path, REnvironment> srcfileEnvironments = new WeakHashMap<>();
     public final List<String> libraryPaths = new ArrayList<>(1);
+    public final Map<Integer, Thread> threads = new ConcurrentHashMap<>();
 
     private final AllocationReporter allocationReporter;