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 e20a312a0f8ed428c7c2e839b3265911f2a8088d..3b7eb5535210ed6c49e3009df37ab3e7cebd05c8 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 b0a17bf8ea6697e396ec98042a2d84c87dbb781a..70ef3a473b7d7372a5385dd56041054e3a2ac8d6 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 642d2b6c0cc701e04af0875b445d3c4e81204cdc..4a9513301d247ad93295a970b0309d897f6b29fd 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 370705c4d0e639a292b92cf8d18b2924ef2e063b..d8c1643b12052a8db1c4015becf7283405dd7ade 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 89e503375ae303eddf7d426985b78bfb7a3ce599..99140fc5bd74d5e31ae6076df1b53a58ada73878 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 ff4d2ddbbb3c55deaf78608f63ae238971e26c22..bef04f3c3bb2f7629ffbe9e884034adaf078065e 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 00c73793904ea592fdf7e7fa41ccdf50e49dc8db..4065174aa36442b2f54e462130143beb42ec970d 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 0000000000000000000000000000000000000000..3c01867839a2c1aed5ef7b411208741d1a71807b --- /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 9762df621c5343f76c61707e1d479458f11752de..7bd99534069cdf92a6d3d3e443d34ee942c5f935 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.