Skip to content
Snippets Groups Projects
Commit 2a3f25c6 authored by Mick Jordan's avatar Mick Jordan
Browse files

RFFI: delay activation until initial context is available

parent 4631be20
Branches
No related tags found
No related merge requests found
Showing with 110 additions and 97 deletions
......@@ -45,8 +45,6 @@ import com.oracle.truffle.r.runtime.FastROptions;
import com.oracle.truffle.r.runtime.RAccuracyInfo;
import com.oracle.truffle.r.runtime.RError;
import com.oracle.truffle.r.runtime.RRuntime;
import com.oracle.truffle.r.runtime.RVersionInfo;
import com.oracle.truffle.r.runtime.TempPathName;
import com.oracle.truffle.r.runtime.Utils;
import com.oracle.truffle.r.runtime.context.Engine.IncompleteSourceException;
import com.oracle.truffle.r.runtime.context.Engine.ParseException;
......@@ -77,9 +75,6 @@ public final class TruffleRLanguage extends TruffleLanguage<RContext> {
Load_RFFIFactory.initialize(true);
Locale.setDefault(Locale.ROOT);
RAccuracyInfo.initialize();
RVersionInfo.initialize();
TempPathName.initialize();
} catch (Throwable t) {
t.printStackTrace();
/*
......
......@@ -145,27 +145,11 @@ public class JNI_Call implements CallRFFI {
private static final boolean ForceRTLDGlobal = false;
public JNI_Call() {
loadLibRLibrary();
initialize();
}
/**
* Load the {@code libR} library. N.B. this library defines some non-JNI global symbols that are
* referenced by C code in R packages. Unfortunately, {@link System#load(String)} uses
* {@code RTLD_LOCAL} with {@code dlopen}, so we have to load the library manually and set
* {@code RTLD_GLOBAL}. However, a {@code dlopen} does not hook the JNI functions into the JVM,
* so we have to do an additional {@code System.load} to achieve that.
*
* Before we do that we must load {@code libjniboot} because the implementation of
* {@link DLLRFFI.DLLRFFINode#dlopen} is called by {@link DLL#loadLibR} which uses JNI!
*/
@TruffleBoundary
private static void loadLibRLibrary() {
String libjnibootPath = LibPaths.getBuiltinLibPath("jniboot");
System.load(libjnibootPath);
String librffiPath = LibPaths.getBuiltinLibPath("R");
DLL.loadLibR(librffiPath);
System.load(librffiPath);
private static void initialize() {
RFFIUtils.initialize();
if (traceEnabled()) {
traceDownCall("initialize");
......
......@@ -29,9 +29,11 @@ import com.oracle.truffle.r.runtime.context.RContext.ContextState;
import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
import com.oracle.truffle.r.runtime.ffi.CRFFI;
import com.oracle.truffle.r.runtime.ffi.CallRFFI;
import com.oracle.truffle.r.runtime.ffi.DLL;
import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
import com.oracle.truffle.r.runtime.ffi.GridRFFI;
import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
import com.oracle.truffle.r.runtime.ffi.LibPaths;
import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
......@@ -53,17 +55,30 @@ public class JNI_RFFIFactory extends RFFIFactory implements RFFI {
public JNI_RFFIFactory() {
}
@Override
protected void initialize(boolean runtime) {
// This must load early as package libraries reference symbols in it.
getCallRFFI();
}
/**
* Placeholder class for context-specific native state.
*/
private static class ContextStateImpl implements RContext.ContextState {
@Override
/**
* For the initial context, load the {@code libR} library. N.B. this library defines some
* non-JNI global symbols that are referenced by C code in R packages. Unfortunately,
* {@link System#load(String)} uses {@code RTLD_LOCAL} with {@code dlopen}, so we have to
* load the library manually and set {@code RTLD_GLOBAL}. However, a {@code dlopen} does not
* hook the JNI functions into the JVM, so we have to do an additional {@code System.load}
* to achieve that.
*
* Before we do that we must load {@code libjniboot} because the implementation of
* {@link DLLRFFI.DLLRFFINode#dlopen} is called by {@link DLL#loadLibR} which uses JNI!
*/
public ContextState initialize(RContext context) {
if (context.isInitial()) {
String libjnibootPath = LibPaths.getBuiltinLibPath("jniboot");
System.load(libjnibootPath);
String librffiPath = LibPaths.getBuiltinLibPath("R");
DLL.loadLibR(librffiPath);
System.load(librffiPath);
}
return this;
}
}
@Override
......
/*
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2017, 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
......@@ -160,9 +160,9 @@ public final class REnvVars implements RContext.ContextState {
/**
* Returns the value of the {@code R_HOME} environment variable (setting it in the unusual case
* where it it is not set by the initiating shell scripts. This is called very early in the
* startup as it is required to access the native libraries, before the initial context is
* initialized and, therefore, before {@link #envVars} is available.
* where it it is not set by the initiating shell scripts. This may be called very early in the
* startup possibly before the initial context is initialized and, therefore, before
* {@link #envVars} is available.
*/
public static String rHome() {
if (rHome == null) {
......@@ -216,8 +216,11 @@ public final class REnvVars implements RContext.ContextState {
private void checkRHome() {
String envRHome = envVars.get(R_HOME);
if (envRHome == null) {
assert rHome != null;
envVars.put(R_HOME, rHome);
envVars.put(R_HOME, rHome());
} else {
if (rHome == null) {
rHome = envRHome;
}
}
}
......
/*
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2017, 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
......@@ -22,6 +22,7 @@
*/
package com.oracle.truffle.r.runtime;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.r.runtime.ffi.BaseRFFI.UtsName;
import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
......@@ -45,8 +46,8 @@ public enum RVersionInfo {
public static final int SERIALIZE_VERSION = (2 << 16) + (3 << 8) + 0;
@CompilationFinal private static final RVersionInfo[] VALUES = RVersionInfo.values();
@CompilationFinal private static final String[] LIST_VALUES = new String[VALUES.length];
@CompilationFinal private static final String[] LIST_NAMES = new String[VALUES.length];
@CompilationFinal private static String[] ListValues;
@CompilationFinal private static String[] ListNames;
private final String listName;
private String value;
......@@ -76,49 +77,56 @@ public enum RVersionInfo {
return Character.toLowerCase(s.charAt(0)) + s.substring(1);
}
public static void initialize() {
UtsName utsname = RFFIFactory.getRFFI().getBaseRFFI().uname();
String osName = toFirstLower(utsname.sysname());
String vendor = osName.equals("darwin") ? "apple" : "unknown";
OS.value = osName + utsname.release();
for (int i = 0; i < VALUES.length; i++) {
RVersionInfo data = VALUES[i];
LIST_NAMES[i] = data.listName;
if (data.value == null) {
switch (data) {
case Platform:
/*
* FIXME In order to match the info in the default packages copied from
* GnuR, this value on Linux has to be x86_64-unknown-linux-gnu
*/
if (osName.equals("linux")) {
if (Arch.value.equals("sparcv9")) {
data.value = "sparc64-unknown-linux-gnu";
private static void initialize() {
if (ListValues == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
ListValues = new String[VALUES.length];
ListNames = new String[VALUES.length];
UtsName utsname = RFFIFactory.getRFFI().getBaseRFFI().uname();
String osName = toFirstLower(utsname.sysname());
String vendor = osName.equals("darwin") ? "apple" : "unknown";
OS.value = osName + utsname.release();
for (int i = 0; i < VALUES.length; i++) {
RVersionInfo data = VALUES[i];
ListNames[i] = data.listName;
if (data.value == null) {
switch (data) {
case Platform:
/*
* FIXME In order to match the info in the default packages copied from
* GnuR, this value on Linux has to be x86_64-unknown-linux-gnu
*/
if (osName.equals("linux")) {
if (Arch.value.equals("sparcv9")) {
data.value = "sparc64-unknown-linux-gnu";
} else {
data.value = "x86_64-unknown-linux-gnu";
}
} else if (osName.toLowerCase().equals("sunos")) {
data.value = "sparc-sun-solaris2.11";
} else {
data.value = "x86_64-unknown-linux-gnu";
data.value = Arch.value + "-" + vendor + "-" + OS.value;
}
} else if (osName.toLowerCase().equals("sunos")) {
data.value = "sparc-sun-solaris2.11";
} else {
data.value = Arch.value + "-" + vendor + "-" + OS.value;
}
break;
case System:
data.value = Arch.value + ", " + OS.value;
break;
default:
data.value = "";
break;
case System:
data.value = Arch.value + ", " + OS.value;
break;
default:
data.value = "";
}
}
ListValues[i] = data.value;
}
LIST_VALUES[i] = data.value;
}
}
public static String[] listNames() {
return LIST_NAMES;
initialize();
return ListNames;
}
public static String[] listValues() {
return LIST_VALUES;
initialize();
return ListValues;
}
}
/*
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2017, 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
......@@ -27,12 +27,14 @@ import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Random;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
/**
*
* As per the GnuR spec, the tempdir() directory is identified on startup.
* As per the GnuR spec, the tempdir() directory is identified on startup. It <b>must</b>be
* initialized before the first RFFI call as the value is available in the R FFI.
*
*/
public class TempPathName {
......@@ -43,9 +45,9 @@ public class TempPathName {
private static String tempDirPath;
private static final Random rand = new Random();
public static void initialize() {
private static void initialize() {
if (tempDirPath == null) {
//
CompilerDirectives.transferToInterpreterAndInvalidate();
final String[] envVars = new String[]{"TMPDIR", "TMP", "TEMP"};
String startingTempDir = null;
for (String envVar : envVars) {
......@@ -78,11 +80,13 @@ public class TempPathName {
}
public static String tempDirPath() {
initialize();
return tempDirPath;
}
@TruffleBoundary
public static String createNonExistingFilePath(String pattern, String tempDir, String fileExt) {
initialize();
while (true) {
StringBuilder sb = new StringBuilder(tempDir);
sb.append(File.separatorChar);
......
......@@ -517,6 +517,11 @@ public final class RContext extends ExecutionContext implements TruffleObject {
attachThread();
state.add(State.ATTACHED);
stateDLL.initialize(this);
stateRFFI = RFFIContextStateFactory.newContextState();
// separate in case initialize calls getStateRFFI()!
stateRFFI.initialize(this);
if (!embedded) {
doEnvOptionsProfileInitialization();
}
......@@ -526,14 +531,10 @@ public final class RContext extends ExecutionContext implements TruffleObject {
stateRConnection.initialize(this);
stateStdConnections.initialize(this);
stateRNG.initialize(this);
stateRFFI = RFFIContextStateFactory.newContextState();
// separate in case initialize calls getStateRFFI()!
stateRFFI.initialize(this);
stateRSerialize.initialize(this);
stateLazyDBCache.initialize(this);
stateInstrumentation.initialize(this);
stateInternalCode.initialize(this);
stateDLL.initialize(this);
state.add(State.INITIALIZED);
if (!embedded) {
......
......@@ -62,9 +62,8 @@ import com.oracle.truffle.r.runtime.rng.user.UserRNG;
*
* The {@code libR} library is a special case, as it is an implementation artifact and not visible
* at the R level. However, it is convenient to manage it in a similar way in this code. It is
* always stored in slot 0 of the list, and is hidden from R code. It is loaded by the
* {@link RFFIFactory} early in the startup, before the initial {@link RContext} is created, by
* {@link #loadLibR}. This should only be called once.
* always stored in slot 0 of the list, and is hidden from R code. It is loaded by {@link #loadLibR}
* , which should only be called once.
*
* As far as possible, all execution is via Truffle {@link Node} classes as, in most cases, the
* invocation is from an existing AST node.
......@@ -74,6 +73,7 @@ public class DLL {
public static class ContextStateImpl implements RContext.ContextState {
private ArrayList<DLLInfo> list;
private RContext context;
private static DLLInfo libRdllInfo;
public static ContextStateImpl newContextState() {
return new ContextStateImpl();
......@@ -86,7 +86,10 @@ public class DLL {
list = context.getParent().stateDLL.list;
} else {
list = new ArrayList<>();
list.add(libRDLLInfo);
if (!context.isInitial()) {
assert list.isEmpty();
list.add(libRdllInfo);
}
}
return this;
}
......@@ -101,6 +104,12 @@ public class DLL {
}
list = null;
}
private void addLibR(DLLInfo dllInfo) {
assert list.isEmpty();
list.add(dllInfo);
libRdllInfo = dllInfo;
}
}
/**
......@@ -354,22 +363,16 @@ public class DLL {
}
/**
* A temporary stash until such time as the initial context is initialized.
*/
private static DLLInfo libRDLLInfo;
/**
* Loads a the {@code libR} library. This is an implementation specific library N.B., when this
* is called for the first time, there is no {@link RContext} available, so we stash the result
* until {@link ContextStateImpl#initialize} is called.
* Loads a the {@code libR} library. This is an implementation specific library.
*/
public static void loadLibR(String path) {
assert libRDLLInfo == null;
RContext context = RContext.getInstance();
Object handle = DLLFFIRootNode.create().getCallTarget().call(DLLFFIRootNode.DLOPEN, path, false, false);
if (handle == null) {
throw Utils.rSuicide("error loading libR from: " + path + "\n");
}
libRDLLInfo = DLLInfo.create(libName(path), path, true, handle, false);
ContextStateImpl dllContext = context.stateDLL;
dllContext.addLibR(DLLInfo.create(libName(path), path, true, handle, false));
}
public static String libName(String absPath) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment