private native Object invoke()

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);
    }