From e7f37ef1f7641a0a6fda78e632fbd2d314412f4b Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Fri, 20 Apr 2018 16:53:18 +0200
Subject: [PATCH] Implement graphics resizing using custom Executor in RContext

---
 .../truffle/r/engine/shell/REmbedded.java     |  2 +-
 .../oracle/truffle/r/launcher/RCommand.java   | 29 +++++++++--
 .../truffle/r/launcher/RscriptCommand.java    |  2 +-
 .../r/library/fastrGrid/GridContext.java      |  7 ++-
 .../r/library/fastrGrid/WindowDevice.java     | 12 +++--
 .../fastrGrid/device/awt/JFrameDevice.java    | 10 +++-
 .../r/nodes/builtin/base/BasePackage.java     |  2 +
 .../nodes/builtin/fastr/FastRGetExecutor.java | 48 +++++++++++++++++++
 .../truffle/r/runtime/context/RContext.java   | 11 ++++-
 9 files changed, 107 insertions(+), 16 deletions(-)
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRGetExecutor.java

diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
index e20a312a0f..3b7eb55352 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
@@ -133,7 +133,7 @@ public class REmbedded {
         RContext ctx = RContext.getInstance();
         ctx.completeEmbeddedInitialization();
         ctx.getRFFI().initializeEmbedded(ctx);
-        int status = RCommand.readEvalPrint(context, consoleHandler);
+        int status = RCommand.readEvalPrint(context, consoleHandler, false);
         context.leave();
         context.close();
         Utils.systemExit(status);
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
index b0a17bf8ea..70ef3a473b 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
@@ -31,6 +31,8 @@ import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 import java.util.function.Supplier;
 
 import org.graalvm.polyglot.Context;
@@ -101,7 +103,7 @@ public class RCommand extends RAbstractLauncher {
                 srcFile = new File(fileOption);
             }
 
-            return readEvalPrint(context, consoleHandler, srcFile);
+            return readEvalPrint(context, consoleHandler, srcFile, true);
         } else {
             return 0;
         }
@@ -209,8 +211,8 @@ public class RCommand extends RAbstractLauncher {
     private static final Source GET_PROMPT = Source.newBuilder("R", ".Internal(getOption('prompt'))", "<prompt>").internal(true).buildLiteral();
     private static final Source GET_CONTINUE_PROMPT = Source.newBuilder("R", ".Internal(getOption('continue'))", "<continue-prompt>").internal(true).buildLiteral();
 
-    public static int readEvalPrint(Context context, ConsoleHandler consoleHandler) {
-        return readEvalPrint(context, consoleHandler, null);
+    public static int readEvalPrint(Context context, ConsoleHandler consoleHandler, boolean useExecutor) {
+        return readEvalPrint(context, consoleHandler, null, useExecutor);
     }
 
     /**
@@ -223,7 +225,11 @@ public class RCommand extends RAbstractLauncher {
      * In case 2, we must implicitly execute a {@code quit("default, 0L, TRUE} command before
      * exiting. So,in either case, we never return.
      */
-    public static int readEvalPrint(Context context, ConsoleHandler consoleHandler, File srcFile) {
+    public static int readEvalPrint(Context context, ConsoleHandler consoleHandler, File srcFile, boolean useExecutor) {
+        ExecutorService executor = null;
+        if (useExecutor) {
+            executor = context.eval(Source.newBuilder("R", ".fastr.getExecutor()", "<get-executor>").internal(true).buildLiteral()).asHostObject();
+        }
         int lastStatus = 0;
         try {
             while (true) { // processing inputs
@@ -253,7 +259,17 @@ public class RCommand extends RAbstractLauncher {
                             } else {
                                 src = Source.newBuilder("R", sb.toString(), "<REPL>").interactive(true).buildLiteral();
                             }
-                            context.eval(src);
+                            if (useExecutor) {
+                                try {
+                                    executor.submit(() -> context.eval(src)).get();
+                                } catch (ExecutionException ex) {
+                                    throw ex.getCause();
+                                }
+                            } else {
+                                context.eval(src);
+                            }
+                        } catch (InterruptedException ex) {
+                            throw fatal("Unexpected interrup error");
                         } catch (PolyglotException e) {
                             if (continuePrompt == null) {
                                 continuePrompt = doEcho ? getContinuePrompt(context) : null;
@@ -300,6 +316,9 @@ public class RCommand extends RAbstractLauncher {
             }
         } catch (ExitException e) {
             return e.code;
+        } catch (Throwable ex) {
+            System.err.println("Unexpected error in REPL");
+            return 1;
         }
     }
 
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
index 642d2b6c0c..4a9513301d 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
@@ -73,7 +73,7 @@ public final class RscriptCommand extends RAbstractLauncher {
             if (fileOption != null) {
                 return executeFile(fileOption);
             } else {
-                return RCommand.readEvalPrint(context, consoleHandler, null);
+                return RCommand.readEvalPrint(context, consoleHandler, null, true);
             }
         } else {
             return 0;
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java
index 370705c4d0..d8c1643b12 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java
@@ -62,14 +62,17 @@ public final class GridContext {
         devices.add(new DeviceAndState(null, null));
     }
 
-    public static GridContext getContext() {
-        RContext rCtx = RContext.getInstance();
+    public static GridContext getContext(RContext rCtx) {
         if (rCtx.gridContext == null) {
             rCtx.gridContext = new GridContext();
         }
         return (GridContext) rCtx.gridContext;
     }
 
+    public static GridContext getContext() {
+        return getContext(RContext.getInstance());
+    }
+
     @TruffleBoundary
     public GridState getGridState() {
         gridState.setDeviceState(devices.get(currentDeviceIdx).state);
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/WindowDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/WindowDevice.java
index 89e503375a..99140fc5bd 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/WindowDevice.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/WindowDevice.java
@@ -42,8 +42,9 @@ public final class WindowDevice {
 
     public static GridDevice createWindowDevice(int width, int height) {
         JFrameDevice frameDevice = new JFrameDevice(width, height);
-        if (RContext.getInstance().hasExecutor()) {
-            frameDevice.setResizeListener(WindowDevice::redrawAll);
+        RContext ctx = RContext.getInstance();
+        if (ctx.hasExecutor()) {
+            frameDevice.setResizeListener(() -> redrawAll(ctx));
         } else {
             noSchedulingSupportWarning();
         }
@@ -54,12 +55,13 @@ public final class WindowDevice {
         throw RError.error(RError.NO_CALLER, Message.GENERIC, "AWT based grid devices are not supported.");
     }
 
-    private static void redrawAll() {
-        RContext ctx = RContext.getInstance();
+    private static void redrawAll(RContext ctx) {
         if (ctx.hasExecutor()) {
             // to be robust we re-check the executor availability
             ctx.schedule(() -> {
-                GridContext.getContext().evalInternalRFunction("redrawAll");
+                Object prev = ctx.getEnv().getContext().enter();
+                GridContext.getContext(ctx).evalInternalRFunction("redrawAll");
+                ctx.getEnv().getContext().leave(prev);
             });
         }
     }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/JFrameDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/JFrameDevice.java
index ff4d2ddbbb..bef04f3c3b 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/JFrameDevice.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/awt/JFrameDevice.java
@@ -55,7 +55,7 @@ public final class JFrameDevice implements GridDevice, ImageSaver {
 
     // This will be drawn on the component
     private BufferedImage componentImage;
-    // Grid draings will be made into this image, may be == componentImage if isOnHold is false
+    // Grid drawings will be made into this image, may be == componentImage if isOnHold is false
     private BufferedImage image;
     // If have we created a new image for buffering while on hold, we keep it to reuse it
     private BufferedImage cachedImage;
@@ -198,6 +198,11 @@ public final class JFrameDevice implements GridDevice, ImageSaver {
         defaultInitGraphics(graphics);
         graphics.clearRect(0, 0, width, height);
         inner = new Graphics2DDevice(graphics, width, height, true);
+        componentImage = image;
+        cachedImage = null;
+        if (isOnHold) {
+            hold();
+        }
     }
 
     private void disposeImageDevice() {
@@ -217,7 +222,10 @@ public final class JFrameDevice implements GridDevice, ImageSaver {
         disposeImageDevice();
         openGraphics2DDevice(newWidth, newHeight);
         if (onResize != null) {
+            // note: onResize action should take care of initiating the repaint
             onResize.run();
+        } else {
+            repaint();
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index 00c7379390..4065174aa3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -127,6 +127,7 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastRRefCountInfo;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRRefCountInfoNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRRegisterFunctions;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRRegisterFunctionsNodeGen;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRGetExecutor;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRSlotAssign;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRSlotAssignNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRSourceInfo;
@@ -436,6 +437,7 @@ public class BasePackage extends RBuiltinPackage {
         add(WithVisible.class, WithVisibleNodeGen::create, WithVisible::createSpecial);
         add(Exists.class, ExistsNodeGen::create);
         add(Expression.class, ExpressionNodeGen::create);
+        add(FastRGetExecutor.class, FastRGetExecutor::new);
         add(FastRContext.R.class, FastRContextFactory.RNodeGen::create);
         add(FastRContext.Rscript.class, FastRContextFactory.RscriptNodeGen::create);
         add(FastRContext.CloseChannel.class, FastRContextFactory.CloseChannelNodeGen::create);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRGetExecutor.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRGetExecutor.java
new file mode 100644
index 0000000000..3c01867839
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRGetExecutor.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 3 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 3 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.builtin.fastr;
+
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.context.RContext;
+
+@RBuiltin(name = ".fastr.getExecutor", kind = PRIMITIVE, behavior = COMPLEX, parameterNames = {})
+public class FastRGetExecutor extends RBuiltinNode.Arg0 {
+
+    static {
+        Casts.noCasts(FastRGetExecutor.class);
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        RContext context = RContext.getInstance();
+        if (!context.hasExecutor()) {
+            context.initExecutor();
+        }
+        return context.getExecutor();
+    }
+}
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 9762df621c..7bd9953406 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
@@ -44,6 +44,7 @@ import java.util.Map;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 import java.util.function.Supplier;
 
 import com.oracle.truffle.api.Assumption;
@@ -246,7 +247,7 @@ public final class RContext {
     private final int multiSlotIndex;
     private TruffleContext truffleContext;
 
-    public Executor executor;
+    private Executor executor;
 
     private final InputStream stdin;
     private final OutputStreamWriter stdout;
@@ -752,10 +753,18 @@ public final class RContext {
         return startParameters;
     }
 
+    public void initExecutor() {
+        this.executor = Executors.newSingleThreadExecutor();
+    }
+
     public boolean hasExecutor() {
         return executor != null;
     }
 
+    public Executor getExecutor() {
+        return this.executor;
+    }
+
     /**
      * Allows another thread to schedule some code to be run in this context's thread. The action
      * can be scheduled only if this context was created with an Executor.
-- 
GitLab