in src/main/java/pemja/core/PythonInterpreter.java [294:426]
private native Object invoke(
long tState, String name, Object[] args, Map<String, Object> kwargs);
/*---------------------------------------------------------------------------------------*/
/*------------------------- Invokes the method of a called object -----------------------*/
private native Object invokeMethodNoArgs(long tState, String obj, String name);
private native Object invokeMethodOneArgBoolean(
long tState, String obj, String name, boolean arg);
private native Object invokeMethodOneArgInt(long tState, String obj, String name, int arg);
private native Object invokeMethodOneArgLong(long tState, String obj, String name, long arg);
private native Object invokeMethodOneArgDouble(
long tState, String obj, String name, double arg);
private native Object invokeMethodOneArgString(
long tState, String obj, String name, String arg);
private native Object invokeMethodOneArgObject(
long tState, String obj, String name, Object arg);
private native Object invokeMethod(long tState, String obj, String name, Object[] args);
/*---------------------------------------------------------------------------------------*/
/**
* Execute an arbitrary number of statements in the JcpThread.
*
* @param tState the JcpThread
* @param code the python code
*/
private native void exec(long tState, String code);
/**
* The main Python interpreter that all sub-interpreters will be created from. The
* MainInterpreter is used to avoid potential deadlocks. Python can deadlock when trying to
* acquire the GIL through methods such as <a href=
* "https://docs.python.org/3/c-api/init.html#c.PyGILState_Ensure">PyGILState_*</a>.
*
* <p>While PemJa does not use those methods, CPython extensions such as numpy do. The deadlock
* can occur if there is more than one PyThreadState per thread. To get around this, the
* MainInterpreter creates a unique thread that initializes Python and keeps this thread around
* forever. This ensures that any new sub-interpreters cannot be created on the same thread as
* the main Python interpreter.
*/
private static class MainInterpreter implements Serializable, AutoCloseable {
private static final long serialVersionUID = 1L;
private static final MainInterpreter instance = new MainInterpreter();
private final CountDownLatch damonThreadStart = new CountDownLatch(1);
private final CountDownLatch damonThreadFinish = new CountDownLatch(1);
/** The flag whether the main interpreter has been started. */
private boolean isStarted = false;
private Thread thread;
private Throwable error;
private MainInterpreter() {}
/** Initializes CPython. */
synchronized void initialize(PythonInterpreterConfig config) {
if (!isStarted) {
CommonUtils.INSTANCE.loadPython(config.getPythonExec());
// We load on a separate thread to try and avoid GIL issues that come about from a
// being on the same thread as the main interpreter.
thread =
new Thread("PemJaMainInterpreter") {
@Override
public void run() {
try {
initialize(config.getPythonHome());
// add shared modules
addToPath(
CommonUtils.INSTANCE.getPemJaModulePath(
config.getPythonExec()));
importModule("redirect_stream");
} catch (Throwable t) {
error = t;
} finally {
damonThreadStart.countDown();
}
try {
damonThreadFinish.await();
} catch (InterruptedException e) {
throw new Error(e);
}
}
};
thread.setDaemon(true);
thread.start();
try {
damonThreadStart.await();
} catch (InterruptedException e) {
if (error == null) {
error = e;
}
}
if (error != null) {
throw new Error(error);
}
isStarted = true;
}
}
@Override
public void close() {
if (thread != null) {
damonThreadFinish.countDown();
}
}
/** Initialize Python Interpreter. */
private native void initialize(String pythonHome);
/** Adds the search path to the main interpreter. */
private native void addToPath(String path);
/** Imports the shared modules to the main interpreter. */
private native void importModule(String module);
}