Skip to content
Snippets Groups Projects
Commit 35057c50 authored by stepan's avatar stepan Committed by Miloslav Metelka
Browse files

Implemented grid-device server.

parent 2a806388
No related branches found
No related tags found
No related merge requests found
Showing
with 1587 additions and 39 deletions
/*
* Copyright (c) 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.library.fastrGrid.server;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.time.LocalTime;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
import com.oracle.truffle.r.library.fastrGrid.device.GridColor;
import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
import com.oracle.truffle.r.library.fastrGrid.device.GridDevice.DeviceCloseException;
import com.oracle.truffle.r.library.fastrGrid.device.GridDevice.ImageInterpolation;
import com.oracle.truffle.r.library.fastrGrid.device.NotSupportedImageFormatException;
import com.oracle.truffle.r.library.fastrGrid.device.remote.RemoteDevice;
import com.oracle.truffle.r.library.fastrGrid.device.remote.RemoteDevice.DeviceType;
import com.oracle.truffle.r.library.fastrGrid.device.remote.RemoteDeviceDataExchange;
import com.oracle.truffle.r.library.fastrGrid.GridContext;
import com.oracle.truffle.r.library.fastrGrid.WindowDevice;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class RemoteDeviceServer {
private static final Logger log = Logger.getLogger(RemoteDevice.class.getName());
private static final String STATUS_HANDLER = "/status";
private static final String QUIT_HANDLER = "/quit";
private static HttpServer server;
private static int lastDeviceId = 0;
private static Map<Integer, GridDevice> id2Device = new ConcurrentHashMap<>();
private static int lastDrawingContextId = 0;
private static Map<Integer, ServerDrawingContext> id2DrawingContext = new ConcurrentHashMap<>();
private static Map<DrawingContext, Integer> drawingContext2id = new ConcurrentHashMap<>();
private static long totalRequestsServiced;
private static long totalBytesRead;
private static long totalBytesWritten;
private static synchronized ServerDrawingContext getDrawingContext(Integer ctxId) {
return getDrawingContextImpl(ctxId);
}
private static ServerDrawingContext getDrawingContextImpl(Integer ctxId) {
ServerDrawingContext ctx = id2DrawingContext.get(ctxId);
assert (ctx != null) : "Unknown or GCed drawing context id=" + ctxId + ", lastDrawingContextId=" + lastDrawingContextId;
return ctx;
}
private static void releaseDrawingContextImpl(Integer ctxId) {
ServerDrawingContext ctx = getDrawingContextImpl(ctxId);
if (ctx.decRefCount()) {
id2DrawingContext.remove(ctxId);
drawingContext2id.remove(ctx);
}
}
public static void main(String[] args) throws IOException {
server = HttpServer.create(new InetSocketAddress(RemoteDevice.SERVER_PORT), 0);
server.createContext(RemoteDevice.COMMAND_HANDLER, new CommandHandler());
server.createContext(STATUS_HANDLER, new StatusAndQuitHandler(false));
server.createContext(QUIT_HANDLER, new StatusAndQuitHandler(true));
server.setExecutor(null); // creates a default executor
server.start();
}
private static class CommandHandler implements HttpHandler {
private RemoteDeviceDataExchange resultEncoder = new RemoteDeviceDataExchange();
@Override
public void handle(HttpExchange exchange) throws IOException {
try {
handleImpl(exchange);
} catch (Throwable t) {
t.printStackTrace();
System.exit(1);
}
}
private void handleImpl(HttpExchange exchange) throws IOException {
totalRequestsServiced++;
InputStream is = exchange.getRequestBody();
byte[] isBuf = new byte[is.available() + 1];
int off = 0;
int len;
try {
while ((len = is.read(isBuf, off, isBuf.length - off)) != -1) {
off += len;
if (off == isBuf.length) {
byte[] newBuf = new byte[isBuf.length + Math.max(isBuf.length, is.available() + 1)];
System.arraycopy(isBuf, 0, newBuf, 0, off);
isBuf = newBuf;
}
}
} finally {
is.close();
}
if (log.isLoggable(Level.FINER)) {
log.finer(RemoteDeviceDataExchange.bytesToString("Server Input: ", isBuf, off));
}
if (off == 0) {
throw new IOException("Empty request to grid server");
}
totalBytesRead += off;
RemoteDeviceDataExchange paramsDecoder = new RemoteDeviceDataExchange(isBuf, off);
byte commandId = paramsDecoder.readByte();
// Optimistically write ok status for all ops (revert if necessary)
resultEncoder.writeByte(RemoteDevice.STATUS_OK);
boolean checkServerClose = false;
RuntimeException rExc = null;
try {
if (commandId == RemoteDevice.CREATE_IMAGE) {
DeviceType type = DeviceType.values()[paramsDecoder.readInt()];
String filename = paramsDecoder.readString();
String fileType = paramsDecoder.readString();
int width = paramsDecoder.readInt();
int height = paramsDecoder.readInt();
int deviceId;
synchronized (RemoteDeviceServer.class) {
deviceId = ++lastDeviceId;
}
GridDevice device;
switch (type) {
case BUFFERED_IMAGE:
try {
device = GridContext.openLocalOrRemoteDevice(filename, fileType, width, height);
} catch (NotSupportedImageFormatException ex) {
deviceId = -1;
device = null;
}
break;
case WINDOW:
device = WindowDevice.createWindowDevice(true, width, height);
break;
default:
throw new AssertionError();
}
if (deviceId != -1) {
id2Device.put(deviceId, device);
}
resultEncoder.writeInt(deviceId);
} else if (commandId == RemoteDevice.CREATE_DRAWING_CONTEXT) {
ServerDrawingContext ctx = new ServerDrawingContext(paramsDecoder);
Integer ctxId = drawingContext2id.get(ctx);
if (ctxId == null) {
synchronized (RemoteDeviceServer.class) {
ctxId = ++lastDrawingContextId;
id2DrawingContext.put(ctxId, ctx);
drawingContext2id.put(ctx, ctxId);
}
} else {
synchronized (RemoteDeviceServer.class) {
ctx = getDrawingContextImpl(ctxId);
ctx.incRefCount();
}
}
resultEncoder.writeInt(ctxId);
} else if (commandId == RemoteDevice.RELEASE_DRAWING_CONTEXT) {
int ctxId = paramsDecoder.readInt();
synchronized (RemoteDeviceServer.class) {
releaseDrawingContextImpl(ctxId);
}
} else {
Integer deviceId = paramsDecoder.readInt();
GridDevice device = id2Device.get(deviceId);
if (device == null) {
throw new IllegalStateException("Grid device for id=" + deviceId + " does not exist on server.");
}
switch (commandId) {
case RemoteDevice.OPEN_NEW_PAGE: {
device.openNewPage();
break;
}
case RemoteDevice.HOLD: {
device.hold();
break;
}
case RemoteDevice.FLUSH: {
device.flush();
break;
}
case RemoteDevice.CLOSE: {
int[] releaseDrawingContextIds = paramsDecoder.readIntArray();
synchronized (RemoteDeviceServer.class) {
for (int i = 0; i < releaseDrawingContextIds.length; i++) {
int ctxId = releaseDrawingContextIds[i];
if (ctxId != 0) {
releaseDrawingContextImpl(ctxId);
}
}
}
id2Device.remove(deviceId);
checkServerClose = true;
String exMsg = null;
try {
device.close();
} catch (DeviceCloseException ex) {
exMsg = ex.getMessage();
}
resultEncoder.writeString(exMsg);
break;
}
case RemoteDevice.DRAW_RECT: {
DrawingContext ctx = getDrawingContext(paramsDecoder.readInt());
double leftX = paramsDecoder.readDouble();
double bottomY = paramsDecoder.readDouble();
double width = paramsDecoder.readDouble();
double height = paramsDecoder.readDouble();
double rotationAnticlockWise = paramsDecoder.readDouble();
device.drawRect(ctx, leftX, bottomY, width, height, rotationAnticlockWise);
break;
}
case RemoteDevice.DRAW_POLY_LINES: {
DrawingContext ctx = getDrawingContext(paramsDecoder.readInt());
double[] x = paramsDecoder.readDoubleArray();
double[] y = paramsDecoder.readDoubleArray();
int startIndex = paramsDecoder.readInt();
int length = paramsDecoder.readInt();
device.drawPolyLines(ctx, x, y, startIndex, length);
break;
}
case RemoteDevice.DRAW_POLYGON: {
DrawingContext ctx = getDrawingContext(paramsDecoder.readInt());
double[] x = paramsDecoder.readDoubleArray();
double[] y = paramsDecoder.readDoubleArray();
int startIndex = paramsDecoder.readInt();
int length = paramsDecoder.readInt();
device.drawPolygon(ctx, x, y, startIndex, length);
break;
}
case RemoteDevice.DRAW_CIRCLE: {
DrawingContext ctx = getDrawingContext(paramsDecoder.readInt());
double centerX = paramsDecoder.readDouble();
double centerY = paramsDecoder.readDouble();
double radius = paramsDecoder.readDouble();
device.drawCircle(ctx, centerX, centerY, radius);
break;
}
case RemoteDevice.DRAW_RASTER: {
double leftX = paramsDecoder.readDouble();
double bottomY = paramsDecoder.readDouble();
double width = paramsDecoder.readDouble();
double height = paramsDecoder.readDouble();
int[] pixels = paramsDecoder.readIntArray();
int pixelsColumnsCount = paramsDecoder.readInt();
ImageInterpolation interpolation = ImageInterpolation.values()[paramsDecoder.readInt()];
device.drawRaster(leftX, bottomY, width, height, pixels, pixelsColumnsCount, interpolation);
break;
}
case RemoteDevice.DRAW_STRING: {
DrawingContext ctx = getDrawingContext(paramsDecoder.readInt());
double leftX = paramsDecoder.readDouble();
double bottomY = paramsDecoder.readDouble();
double rotationAnticlockWise = paramsDecoder.readDouble();
String text = paramsDecoder.readString();
device.drawString(ctx, leftX, bottomY, rotationAnticlockWise, text);
break;
}
case RemoteDevice.GET_WIDTH: {
resultEncoder.writeDouble(device.getWidth());
break;
}
case RemoteDevice.GET_HEIGHT: {
resultEncoder.writeDouble(device.getHeight());
break;
}
case RemoteDevice.GET_NATIVE_WIDTH: {
resultEncoder.writeDouble(device.getNativeWidth());
break;
}
case RemoteDevice.GET_NATIVE_HEIGHT: {
resultEncoder.writeDouble(device.getNativeHeight());
break;
}
case RemoteDevice.GET_STRING_WIDTH: {
DrawingContext ctx = getDrawingContext(paramsDecoder.readInt());
String text = paramsDecoder.readString();
resultEncoder.writeDouble(device.getStringWidth(ctx, text));
break;
}
case RemoteDevice.GET_STRING_HEIGHT: {
DrawingContext ctx = getDrawingContext(paramsDecoder.readInt());
String text = paramsDecoder.readString();
resultEncoder.writeDouble(device.getStringHeight(ctx, text));
break;
}
default:
throw new IllegalStateException("Invalid requestId=" + commandId);
}
}
} catch (RuntimeException ex) {
rExc = ex;
}
byte[] osBuf = resultEncoder.resetWrite();
if (rExc != null) {
resultEncoder.writeByte(RemoteDevice.STATUS_SERVER_ERROR);
osBuf = resultEncoder.resetWrite();
}
if (log.isLoggable(Level.FINER)) {
log.finer(RemoteDeviceDataExchange.bytesToString("Server Output: ", osBuf, osBuf.length));
}
exchange.sendResponseHeaders(200, osBuf.length);
OutputStream os = exchange.getResponseBody();
try {
os.write(osBuf);
} finally {
os.close();
}
totalBytesWritten += osBuf.length;
exchange.close();
if (checkServerClose && rExc == null && id2Device.isEmpty()) {
log.fine("Server closing automatically after last device was closed.");
server.stop(0);
System.exit(0);
}
if (rExc != null) {
throw rExc;
}
}
}
private static final class ServerDrawingContext implements DrawingContext {
private final byte[] lineType;
private final double lineWidth;
private final GridLineJoin lineJoin;
private final GridLineEnd lineEnd;
private final double lineMitre;
private final GridColor color;
private final double fontSize;
private final GridFontStyle fontStyle;
private final String fontFamily;
private final double lineHeight;
private final GridColor fillColor;
private int hash;
private int refCount = 1;
ServerDrawingContext(RemoteDeviceDataExchange paramsDecoder) {
byte[] lineTypeRead = paramsDecoder.readByteArray();
if (lineTypeRead == null) {
lineType = DrawingContext.GRID_LINE_BLANK;
} else if (lineTypeRead.length == 0) {
lineType = DrawingContext.GRID_LINE_SOLID;
} else {
lineType = lineTypeRead;
}
lineWidth = paramsDecoder.readDouble();
lineJoin = GridLineJoin.values()[paramsDecoder.readInt()];
lineEnd = GridLineEnd.values()[paramsDecoder.readInt()];
lineMitre = paramsDecoder.readDouble();
color = GridColor.fromRawValue(paramsDecoder.readInt());
fontSize = paramsDecoder.readDouble();
fontStyle = GridFontStyle.values()[paramsDecoder.readInt()];
fontFamily = paramsDecoder.readString();
lineHeight = paramsDecoder.readDouble();
fillColor = GridColor.fromRawValue(paramsDecoder.readInt());
}
@Override
public byte[] getLineType() {
return lineType;
}
@Override
public double getLineWidth() {
return lineWidth;
}
@Override
public GridLineJoin getLineJoin() {
return lineJoin;
}
@Override
public GridLineEnd getLineEnd() {
return lineEnd;
}
@Override
public double getLineMitre() {
return lineMitre;
}
@Override
public GridColor getColor() {
return color;
}
@Override
public double getFontSize() {
return fontSize;
}
@Override
public GridFontStyle getFontStyle() {
return fontStyle;
}
@Override
public String getFontFamily() {
return fontFamily;
}
@Override
public double getLineHeight() {
return lineHeight;
}
@Override
public GridColor getFillColor() {
return fillColor;
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
h = lineType.length;
for (int i = 0; i < lineType.length; i++) {
h = (h << 8) ^ lineType[i];
}
h ^= Double.hashCode(lineWidth);
h ^= lineJoin.ordinal();
h ^= lineEnd.ordinal();
h ^= Double.hashCode(lineMitre);
h ^= color.getRawValue();
h ^= Double.hashCode(fontSize);
h ^= fontStyle.ordinal();
h ^= (fontFamily != null) ? fontFamily.hashCode() : 0;
h ^= Double.hashCode(lineHeight);
h ^= fillColor.getRawValue();
hash = h;
}
return h;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ServerDrawingContext) {
ServerDrawingContext ctx = (ServerDrawingContext) obj;
byte[] ctxLT = ctx.lineType;
if (lineType != ctxLT) {
if (lineType != null && ctxLT != null && lineType.length == ctxLT.length) {
for (int i = ctxLT.length - 1; i >= 0; i--) {
if (lineType[i] != ctxLT[i]) {
return false;
}
}
} else {
return false;
}
}
if (lineWidth != ctx.lineWidth || lineJoin != ctx.lineJoin ||
lineEnd != ctx.lineEnd || lineMitre != ctx.lineMitre ||
!color.equals(ctx.color) || fontSize != ctx.fontSize ||
fontStyle != ctx.fontStyle || lineHeight != ctx.lineHeight ||
!fillColor.equals(ctx.fillColor)) {
return false;
}
if (fontFamily != ctx.fontFamily) {
return (fontFamily != null && fontFamily.equals(ctx.fontFamily));
}
return true;
}
return false;
}
void incRefCount() {
refCount++;
}
boolean decRefCount() {
return (--refCount == 0);
}
}
private static class StatusAndQuitHandler implements HttpHandler {
private final boolean handleQuit;
StatusAndQuitHandler(boolean handleQuitArg) {
this.handleQuit = handleQuitArg;
}
@Override
public void handle(HttpExchange exchange) throws IOException {
OutputStream os = exchange.getResponseBody();
String response = (handleQuit ? LocalTime.now().toString() + ": Grid server stopped. Exit.\n" : "") +
"Total devices created: " + lastDeviceId + ", active: " + id2Device.size() +
"\nTotal DrawingContexts created: " + lastDrawingContextId + ", active: " + id2DrawingContext.size() +
"\nTotal requests serviced: " + totalRequestsServiced +
"\nTotal bytes read: " + totalBytesRead + ", written: " + totalBytesWritten;
byte[] responseBytes = response.getBytes();
exchange.sendResponseHeaders(200, responseBytes.length);
os.write(responseBytes);
os.close();
if (handleQuit) {
server.stop(0);
System.exit(0);
}
}
}
}
......@@ -33,7 +33,8 @@ import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
import com.oracle.truffle.r.library.fastrGrid.device.GridDevice.DeviceCloseException;
import com.oracle.truffle.r.library.fastrGrid.device.SVGDevice;
import com.oracle.truffle.r.library.fastrGrid.device.awt.BufferedImageDevice;
import com.oracle.truffle.r.library.fastrGrid.device.awt.BufferedImageDevice.NotSupportedImageFormatException;
import com.oracle.truffle.r.library.fastrGrid.device.NotSupportedImageFormatException;
import com.oracle.truffle.r.library.fastrGrid.device.remote.RemoteDevice;
import com.oracle.truffle.r.library.fastrGrid.grDevices.FileDevUtils;
import com.oracle.truffle.r.library.fastrGrid.graphics.RGridGraphicsAdapter;
import com.oracle.truffle.r.runtime.FastRConfig;
......@@ -106,6 +107,14 @@ public final class GridContext {
return getContext(RContext.getInstance());
}
public static GridDevice openLocalOrRemoteDevice(String filename, String fileType, int width, int height) throws NotSupportedImageFormatException {
if (FastRConfig.UseRemoteGridAwtDevice) {
return RemoteDevice.open(filename, fileType, width, height);
} else {
return BufferedImageDevice.open(filename, fileType, width, height);
}
}
@TruffleBoundary
public GridState getGridState() {
gridState.setDeviceState(devices.get(currentDeviceIdx).state);
......@@ -153,7 +162,7 @@ public final class GridContext {
if (!FastRConfig.InternalGridAwtSupport) {
throw awtNotSupported();
}
setCurrentDevice(defaultDev, WindowDevice.createWindowDevice());
setCurrentDevice(defaultDev, WindowDevice.createWindowDevice(false, GridDevice.DEFAULT_WIDTH, GridDevice.DEFAULT_HEIGHT));
} else if (defaultDev.equals("svg")) {
String filename = "Rplot%03d.svg";
SVGDevice svgDevice = new SVGDevice(FileDevUtils.formatInitialFilename(filename), GridDevice.DEFAULT_WIDTH, GridDevice.DEFAULT_HEIGHT);
......@@ -200,12 +209,9 @@ public final class GridContext {
}
private void safeOpenImageDev(String filename, String formatName) {
if (!FastRConfig.InternalGridAwtSupport) {
throw awtNotSupported();
}
BufferedImageDevice dev = null;
GridDevice dev = null;
try {
dev = BufferedImageDevice.open(FileDevUtils.formatInitialFilename(filename), formatName, GridDevice.DEFAULT_WIDTH, GridDevice.DEFAULT_HEIGHT);
dev = openLocalOrRemoteDevice(FileDevUtils.formatInitialFilename(filename), formatName, GridDevice.DEFAULT_WIDTH, GridDevice.DEFAULT_HEIGHT);
} catch (NotSupportedImageFormatException e) {
throw RInternalError.shouldNotReachHere("Device format " + formatName + " should be supported.");
}
......
......@@ -24,7 +24,9 @@ package com.oracle.truffle.r.library.fastrGrid;
import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
import com.oracle.truffle.r.library.fastrGrid.device.awt.JFrameDevice;
import com.oracle.truffle.r.library.fastrGrid.device.remote.RemoteDevice;
import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
import com.oracle.truffle.r.runtime.FastRConfig;
import com.oracle.truffle.r.runtime.RCaller;
import com.oracle.truffle.r.runtime.RError;
import com.oracle.truffle.r.runtime.RError.Message;
......@@ -41,20 +43,23 @@ public final class WindowDevice {
// only static members
}
public static GridDevice createWindowDevice() {
return createWindowDevice(GridDevice.DEFAULT_WIDTH, GridDevice.DEFAULT_HEIGHT);
}
public static GridDevice createWindowDevice(int width, int height) {
JFrameDevice frameDevice = new JFrameDevice(width, height);
RContext ctx = RContext.getInstance();
if (ctx.hasExecutor()) {
frameDevice.setResizeListener(() -> redrawAll(ctx));
frameDevice.setCloseListener(() -> devOff(ctx));
} else {
public static GridDevice createWindowDevice(boolean byGridServer, int width, int height) {
if (FastRConfig.UseRemoteGridAwtDevice) {
noSchedulingSupportWarning();
return RemoteDevice.createWindowDevice(width, height);
} else {
JFrameDevice frameDevice = new JFrameDevice(width, height);
RContext ctx;
if (!byGridServer && ((ctx = RContext.getInstance()) != null) && ctx.hasExecutor() && !FastRConfig.UseRemoteGridAwtDevice) {
frameDevice.setResizeListener(() -> redrawAll(ctx));
frameDevice.setCloseListener(() -> devOff(ctx));
} else {
if (!byGridServer) {
noSchedulingSupportWarning();
}
}
return frameDevice;
}
return frameDevice;
}
public static RError awtNotSupported() {
......@@ -103,7 +108,8 @@ public final class WindowDevice {
}
private static void noSchedulingSupportWarning() {
// Note: the PolyglotEngine was not built with an Executor
RError.warning(RError.NO_CALLER, Message.GENERIC, "Grid cannot resize the drawings. If you resize the window, the content will be lost.");
// Note: the PolyglotEngine was not built with an Executor or we use remote grid device
RError.warning(RError.NO_CALLER, Message.GENERIC, "Grid cannot resize the drawings. If you resize the window, the content will be lost. " +
"You can redraw the contents using: 'popViewport(0, recording = FALSE); grid:::draw.all()'.");
}
}
/*
* Copyright (c) 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.library.fastrGrid.device;
public class NotSupportedImageFormatException extends Exception {
private static final long serialVersionUID = 1182697755931636217L;
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
......@@ -22,6 +22,7 @@
*/
package com.oracle.truffle.r.library.fastrGrid.device.awt;
import com.oracle.truffle.r.library.fastrGrid.device.NotSupportedImageFormatException;
import static java.awt.image.BufferedImage.TYPE_INT_RGB;
import java.awt.Color;
......@@ -102,12 +103,4 @@ public final class BufferedImageDevice extends Graphics2DDevice implements FileG
return false;
}
public static class NotSupportedImageFormatException extends Exception {
private static final long serialVersionUID = 1182697755931636217L;
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
}
/*
* Copyright (c) 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.library.fastrGrid.device.remote;
import com.oracle.truffle.r.runtime.RInternalError;
import java.nio.charset.StandardCharsets;
public class RemoteDeviceDataExchange {
public static String bytesToString(String title, byte[] buf, int limit) {
StringBuilder sb = new StringBuilder(title.length() + (limit << 2));
sb.append(title).append("0x");
for (int i = 0; i < limit; i++) {
int b = buf[i] & 0xFF;
sb.append("0123456789ABCDEF".charAt(b >>> 4));
sb.append("0123456789ABCDEF".charAt(b & 0x0F));
}
return sb.toString();
}
private static final int DEFAULT_BUF_SIZE = 64;
private byte[] buf;
private int index;
private int limit;
public RemoteDeviceDataExchange() {
this.buf = new byte[DEFAULT_BUF_SIZE];
}
public RemoteDeviceDataExchange(byte[] readBuf, int limit) {
this.buf = readBuf;
this.limit = limit;
}
public void writeInt(int value) {
ensureCapacity(4);
buf[index++] = (byte) (value >>> 24);
buf[index++] = (byte) (value >> 16);
buf[index++] = (byte) (value >> 8);
buf[index++] = (byte) value;
}
public int readInt() {
ensureData(4);
return ((buf[index++] & 0xff) << 24 |
(buf[index++] & 0xff) << 16 |
(buf[index++] & 0xff) << 8 |
(buf[index++] & 0xff));
}
public void writeIntArray(int[] value) {
if (value != null) {
int len = value.length;
ensureCapacity(4 + (len << 2));
writeInt(len);
for (int i = 0; i < len; i++) {
writeInt(value[i]);
}
} else {
writeInt(-1);
}
}
public int[] readIntArray() {
int len = readInt();
if (len == -1) {
return null;
}
ensureData(len << 2);
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
arr[i] = readInt();
}
return arr;
}
public void writeByte(byte value) {
ensureCapacity(1);
buf[index++] = value;
}
public byte readByte() {
ensureData(1);
return buf[index++];
}
public void writeByteArray(byte[] value) {
if (value != null) {
int len = value.length;
ensureCapacity(4 + len);
writeInt(len);
System.arraycopy(value, 0, buf, index, len);
index += len;
} else {
writeInt(-1);
}
}
public byte[] readByteArray() {
int len = readInt();
if (len == -1) {
return null;
}
ensureData(len);
byte[] arr = new byte[len];
System.arraycopy(buf, index, arr, 0, len);
index += len;
return arr;
}
public void writeDouble(double value) {
ensureCapacity(8);
long valueBits = Double.doubleToRawLongBits(value);
buf[index++] = (byte) (valueBits >>> 56);
buf[index++] = (byte) ((valueBits >> 48) & 0xff);
buf[index++] = (byte) ((valueBits >> 40) & 0xff);
buf[index++] = (byte) ((valueBits >> 32) & 0xff);
buf[index++] = (byte) ((valueBits >> 24) & 0xff);
buf[index++] = (byte) ((valueBits >> 16) & 0xff);
buf[index++] = (byte) ((valueBits >> 8) & 0xff);
buf[index++] = (byte) (valueBits & 0xff);
}
public double readDouble() {
long bits = ((long) (buf[index++] & 0xff) << 56 | (long) (buf[index++] & 0xff) << 48 | (long) (buf[index++] & 0xff) << 40 | (long) (buf[index++] & 0xff) << 32 |
(long) (buf[index++] & 0xff) << 24 | (long) (buf[index++] & 0xff) << 16 | (long) (buf[index++] & 0xff) << 8 | buf[index++] & 0xff);
return Double.longBitsToDouble(bits);
}
public void writeDoubleArray(double[] value) {
if (value != null) {
int len = value.length;
ensureCapacity(4 + (len << 3));
writeInt(len);
for (int i = 0; i < len; i++) {
writeDouble(value[i]);
}
} else {
writeInt(-1);
}
}
public double[] readDoubleArray() {
int len = readInt();
if (len == -1) {
return null;
}
ensureData(len << 3);
double[] arr = new double[len];
for (int i = 0; i < len; i++) {
arr[i] = readDouble();
}
return arr;
}
public void writeString(String value) {
if (value != null) {
boolean simple = true;
for (int i = 0; i < value.length(); i++) {
if (value.charAt(i) >= 0x80) {
simple = false;
break;
}
}
if (simple && value.length() <= buf.length) {
writeInt(value.length());
ensureCapacity(value.length());
for (int i = 0; i < value.length(); i++) {
buf[index++] = (byte) value.charAt(i);
}
} else {
byte[] bytes = value.getBytes();
int bytesLen = bytes.length;
ensureCapacity(bytesLen + 4);
writeInt(bytesLen);
System.arraycopy(bytes, 0, buf, index, bytesLen);
index += bytesLen;
}
} else { // value == null
writeInt(-1);
}
}
public String readString() {
int strLen = readInt();
if (strLen == -1) {
return null;
}
boolean simple = true;
for (int i = 0; i < strLen; i++) {
byte b = buf[index + i];
if (b < 0) {
simple = false;
break;
}
}
String result;
if (simple) {
@SuppressWarnings("deprecation")
String s = new String(buf, 0, index, strLen);
result = s;
} else {
result = new String(buf, index, strLen, StandardCharsets.UTF_8);
}
index += strLen;
return result;
}
/**
* Grab all bytes written so far and return them as byte array and then reset write index to
* zero for fresh writing.
*
* @return bytes written prior call to this method.
*/
public byte[] resetWrite() {
byte[] result = new byte[index];
System.arraycopy(buf, 0, result, 0, index);
index = 0;
return result;
}
public boolean isEmpty() {
return (index == 0);
}
public boolean isReadFinished() {
return (index == buf.length);
}
private void ensureCapacity(int nBytes) {
int requireLen = index + nBytes;
if (requireLen > buf.length) {
byte[] newBuf = new byte[Math.max(requireLen, buf.length << 1)];
System.arraycopy(buf, 0, newBuf, 0, index);
buf = newBuf;
}
}
private void ensureData(int nBytes) {
if (index + nBytes > limit) {
throw RInternalError.unimplemented("Unexpected EOF: " + (nBytes - limit) + " more bytes expected.");
}
}
}
......@@ -31,8 +31,7 @@ import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.r.library.fastrGrid.GridContext;
import com.oracle.truffle.r.library.fastrGrid.WindowDevice;
import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
import com.oracle.truffle.r.library.fastrGrid.device.awt.BufferedImageDevice;
import com.oracle.truffle.r.library.fastrGrid.device.awt.BufferedImageDevice.NotSupportedImageFormatException;
import com.oracle.truffle.r.library.fastrGrid.device.NotSupportedImageFormatException;
import com.oracle.truffle.r.library.fastrGrid.device.awt.Graphics2DDevice;
import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
import com.oracle.truffle.r.runtime.FastRConfig;
......@@ -94,7 +93,7 @@ public final class InitWindowedDevice extends RExternalBuiltinNode {
}
// otherwise create the window ourselves
GridDevice device = WindowDevice.createWindowDevice(width, height);
GridDevice device = WindowDevice.createWindowDevice(false, width, height);
String name = isFastRDevice ? "awt" : "X11cairo";
GridContext.getContext().setCurrentDevice(name, device);
return RNull.instance;
......@@ -104,7 +103,7 @@ public final class InitWindowedDevice extends RExternalBuiltinNode {
String formatName = name.substring(0, name.indexOf("::"));
String filename = name.substring(name.lastIndexOf(':') + 1);
try {
BufferedImageDevice device = BufferedImageDevice.open(FileDevUtils.formatInitialFilename(filename), formatName, width, height);
GridDevice device = GridContext.openLocalOrRemoteDevice(FileDevUtils.formatInitialFilename(filename), formatName, width, height);
GridContext.getContext().setCurrentDevice(formatName.toUpperCase(), device, filename);
} catch (NotSupportedImageFormatException e) {
throw error(Message.GENERIC, String.format("Format '%s' is not supported.", formatName));
......
......@@ -28,6 +28,8 @@ public final class FastRConfig {
*/
public static final boolean InternalGridAwtSupport;
public static final boolean UseRemoteGridAwtDevice;
/**
* Umbrella option, which changes default values of other options in a way that FastR will not
* invoke any native code directly and other potentially security sensitive operations are
......@@ -59,10 +61,12 @@ public final class FastRConfig {
InternalGridAwtSupport = false;
UseMXBeans = false;
UseNativeEventLoop = false;
UseRemoteGridAwtDevice = false;
} else {
InternalGridAwtSupport = getBoolean("fastr.internal.grid.awt.support");
UseMXBeans = getBoolean("fastr.internal.usemxbeans");
UseNativeEventLoop = getBoolean("fastr.internal.usenativeeventloop");
InternalGridAwtSupport = getBooleanOrTrue("fastr.internal.grid.awt.support");
UseMXBeans = getBooleanOrTrue("fastr.internal.usemxbeans");
UseNativeEventLoop = getBooleanOrTrue("fastr.internal.usenativeeventloop");
UseRemoteGridAwtDevice = getBooleanOrFalse("fastr.use.remote.grid.awt.device");
}
DefaultDownloadMethod = System.getProperty("fastr.internal.defaultdownloadmethod");
}
......@@ -71,7 +75,12 @@ public final class FastRConfig {
// only static fields
}
private static boolean getBoolean(String propName) {
private static boolean getBooleanOrFalse(String propName) {
String val = System.getProperty(propName);
return val != null && val.equals("true");
}
private static boolean getBooleanOrTrue(String propName) {
String val = System.getProperty(propName);
return val == null || val.equals("true");
}
......
......@@ -107,6 +107,11 @@ def do_run_r(args, command, extraVmArgs=None, jdk=None, **kwargs):
vmArgs.extend(_command_class_dict[command.lower()])
return mx.run_java(vmArgs + args, jdk=jdk, **kwargs)
def run_grid_server(args, **kwargs):
vmArgs = mx.get_runtime_jvm_args(['GRID_DEVICE_REMOTE_SERVER'], jdk=get_default_jdk())
vmArgs.append('com.oracle.truffle.r.library.fastrGrid.device.remote.server.RemoteDeviceServer')
return mx.run_java(vmArgs + args, jdk=get_default_jdk(), **kwargs)
def r_classpath(args):
print mx.classpath('FASTR', jdk=mx.get_jdk())
......@@ -594,6 +599,7 @@ _commands = {
'R' : [rshell, '[options]'],
'rscript' : [rscript, '[options]'],
'Rscript' : [rscript, '[options]'],
'gridserver' : [run_grid_server, ''],
'rtestgen' : [testgen, ''],
'rgate' : [rgate, ''],
'rutsimple' : [ut_simple, ['options']],
......
......@@ -7,11 +7,11 @@ Requires = tool:truffle
JavaArgs = \
-Dfastr.resource.factory.class=com.oracle.truffle.r.nodes.builtin.EagerResourceHandlerFactory \
-Dfastr.internal.grid.awt.support=false \
-Dfastr.internal.usemxbeans=false \
-Dfastr.internal.usenativeeventloop=false \
-Dfastr.internal.defaultdownloadmethod=wget \
-Dfastr.internal.ignorejvmargs=true \
-Dfastr.use.remote.grid.awt.device=true \
-Xmx6G
LauncherClass = com.oracle.truffle.r.launcher.RMain
......@@ -19,4 +19,5 @@ LauncherClassPath = lib/graalvm/launcher-common.jar:languages/R/fastr-launcher.j
Args = -H:MaxRuntimeCompileMethods=8000 \
-H:-TruffleCheckFrameImplementation \
-H:+TruffleCheckNeverPartOfCompilation
-H:+TruffleCheckNeverPartOfCompilation \
-H:EnableURLProtocols=http
......@@ -311,6 +311,20 @@ suite = {
},
"com.oracle.truffle.r.library.fastrGrid.server" : {
"sourceDirs" : ["src"],
"dependencies" : [
"com.oracle.truffle.r.library",
],
"annotationProcessors" : [
],
"checkstyle" : "com.oracle.truffle.r.runtime",
"javaCompliance" : "1.8",
"workingSets" : "FastR",
"jacoco" : "include",
},
"com.oracle.truffle.r.release" : {
"sourceDirs" : ["src"],
"buildDependencies" : ["com.oracle.truffle.r.native.recommended"],
......@@ -396,6 +410,20 @@ suite = {
],
},
"GRID_DEVICE_REMOTE_SERVER" : {
"description" : "remote server for grid device",
"dependencies" : [
"com.oracle.truffle.r.library.fastrGrid.server",
],
"mainClass" : "com.oracle.truffle.r.library.fastrGrid.server.RemoteDeviceServer",
"exclude" : [
"truffle:JLINE",
"ANTLR-3.5",
"GNUR",
"XZ-1.6",
],
},
"FASTR_UNIT_TESTS" : {
"description" : "unit tests",
"dependencies" : [
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment