Skip to content

Commit

Permalink
Add some options to control the shutdown sequence
Browse files Browse the repository at this point in the history
  • Loading branch information
Thrameos committed Feb 12, 2021
1 parent 5d7c668 commit 800a363
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 25 deletions.
16 changes: 14 additions & 2 deletions jpype/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ def decorate(func):


def isJVMStarted():
""" This method is horribly named. It should be named isJVMRunning as
isJVMStarted would seem to imply that the JVM was started at some
point without regard to whether it has been shutdown.
"""
return _jpype.isStarted()


Expand Down Expand Up @@ -327,17 +331,25 @@ def shutdownJVM():
restart the JVM after being terminated.
"""
import threading
import jpype.config
if threading.current_thread() is not threading.main_thread():
raise RuntimeError("Shutdown must be called from main thread")
_jpype.shutdown()
if _jpype.isStarted():
_jpype.JPypeContext.freeResources = jpype.config.free_resources
_jpype.shutdown(jpype.config.destroy_jvm, jpype.config.free_jvm)


# In order to shutdown cleanly we need the reference queue stopped
# otherwise, we can experience a crash if a Java thread is waiting
# for the GIL.
def _JTerminate():
try:
_jpype.shutdown()
import jpype.config
# We are exiting anyway so no need to free resources
if _jpype.isStarted():
_jpype.JPypeContext.freeResources = False
if jpype.config.onexit:
_jpype.shutdown(jpype.config.destroy_jvm, False)
except RuntimeError:
pass

Expand Down
44 changes: 44 additions & 0 deletions jpype/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# *****************************************************************************
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# See NOTICE file for details.
#
# *****************************************************************************

# This files holds hints used to control behavior of the JVM
# It is global variables rather than options to a function so that
# they can be set a module at any time rather than requiring arguments
# for a function.
#
# These options are to be treated as hints which may or may not be supported
# in future versions. Setting an unsupported option shall have no effect.
# Variables shall not be removed even if deprecated so that conditions
# based on these will be valid in all future versions.
#

onexit = True
"""If this is False, the JVM not will be notified of Python exit. Java will not execute any
resource cleanup routines."""

destroy_jvm = True
""" If this is False, the JVM will not execute any cleanup routines and memory will
not be freed."""

free_resources = True
""" If this is False, the resources will be allowed to leak after the shutdown call.
"""

free_jvm = True
""" If this is False, the shared library will not be unloaded which leaks memory.
"""
2 changes: 1 addition & 1 deletion native/common/include/jp_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class JPContext
bool ignoreUnrecognized, bool convertStrings, bool interrupt);
void attachJVM(JNIEnv* env);
void initializeResources(JNIEnv* env, bool interrupt);
void shutdownJVM();
void shutdownJVM(bool destroyJVM, bool freeJVM);
void attachCurrentThread();
void attachCurrentThreadAsDaemon();
bool isThreadAttached();
Expand Down
17 changes: 11 additions & 6 deletions native/common/jp_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ void JPContext::onShutdown()
m_Running = false;
}

void JPContext::shutdownJVM()
void JPContext::shutdownJVM(bool destroyJVM, bool freeJVM)
{
JP_TRACE_IN("JPContext::shutdown");
if (m_JavaVM == NULL)
Expand All @@ -383,12 +383,21 @@ void JPContext::shutdownJVM()
// JP_RAISE(PyExc_RuntimeError, "Cannot shutdown from embedded Python");

// Wait for all non-demon threads to terminate
JP_TRACE("Destroy JVM");
if (destroyJVM)
{
JP_TRACE("Destroy JVM");
JPPyCallRelease call;
m_JavaVM->DestroyJavaVM();
}

// unload the jvm library
if (freeJVM)
{
JP_TRACE("Unload JVM");
m_JavaVM = NULL;
JPPlatformAdapter::getAdapter()->unloadLibrary();
}

JP_TRACE("Delete resources");
for (std::list<JPResource*>::iterator iter = m_Resources.begin();
iter != m_Resources.end(); ++iter)
Expand All @@ -397,10 +406,6 @@ void JPContext::shutdownJVM()
}
m_Resources.clear();

// unload the jvm library
JP_TRACE("Unload JVM");
m_JavaVM = NULL;
JPPlatformAdapter::getAdapter()->unloadLibrary();
JP_TRACE_OUT;
}

Expand Down
30 changes: 17 additions & 13 deletions native/java/org/jpype/JPypeContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public class JPypeContext
private final AtomicInteger shutdownFlag = new AtomicInteger();
private final List<Thread> shutdownHooks = new ArrayList<>();
private final List<Runnable> postHooks = new ArrayList<>();
public static boolean freeResources = true;

static public JPypeContext getInstance()
{
Expand Down Expand Up @@ -241,20 +242,23 @@ private void shutdown()
{
}

// Release all Python references
try
{
JPypeReferenceQueue.getInstance().stop();
} catch (Throwable th)
{
}

// Release any C++ resources
try
{
this.typeManager.shutdown();
} catch (Throwable th)
if (freeResources)
{
// Release all Python references
try
{
JPypeReferenceQueue.getInstance().stop();
} catch (Throwable th)
{
}

// Release any C++ resources
try
{
this.typeManager.shutdown();
} catch (Throwable th)
{
}
}

// Execute post hooks
Expand Down
12 changes: 9 additions & 3 deletions native/python/pyjp_module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,16 @@ static PyObject* PyJPModule_startup(PyObject* module, PyObject* pyargs)
JP_PY_CATCH(NULL);
}

static PyObject* PyJPModule_shutdown(PyObject* obj)
static PyObject* PyJPModule_shutdown(PyObject* obj, PyObject* pyargs, PyObject* kwargs)
{
JP_PY_TRY("PyJPModule_shutdown");
JPContext_global->shutdownJVM();
char destroyJVM = true;
char freeJVM = true;

if (!PyArg_ParseTuple(pyargs, "bb", &destroyJVM, &freeJVM))
return NULL;

JPContext_global->shutdownJVM(destroyJVM, freeJVM);
Py_RETURN_NONE;
JP_PY_CATCH(NULL);
}
Expand Down Expand Up @@ -660,7 +666,7 @@ static PyMethodDef moduleMethods[] = {
#else
{"startup", (PyCFunction) PyJPModule_startup, METH_VARARGS, ""},
// {"attach", (PyCFunction) (&PyJPModule_attach), METH_VARARGS, ""},
{"shutdown", (PyCFunction) PyJPModule_shutdown, METH_NOARGS, ""},
{"shutdown", (PyCFunction) PyJPModule_shutdown, METH_VARARGS, ""},
#endif
{"_getClass", (PyCFunction) PyJPModule_getClass, METH_O, ""},
{"_hasClass", (PyCFunction) PyJPModule_hasClass, METH_O, ""},
Expand Down

0 comments on commit 800a363

Please sign in to comment.