jnius: fix push/pop jnienv in order to avoid GIL going on using the previous thread env while we are still on the current thread

This commit is contained in:
Mathieu Virbel 2013-06-24 18:36:58 +02:00
parent eedf301d60
commit 8349037dd3
2 changed files with 79 additions and 66 deletions

View File

@ -11,17 +11,22 @@ cdef JNIEnv *get_jnienv():
env_stack = 0
return env_current
cdef void push_jnienv(JNIEnv *env):
cdef void push_jnienv(JNIEnv *env) nogil:
global env_stacks, env_stack, env_current
if env_stack == 255:
raise Exception('Jnius: cannot push JNI env, too many entries')
with gil:
print('ERROR: Jnius cannot push JNI env, too many entries')
return
env_stack += 1
env_stacks[env_stack] = env_current = env
cdef void pop_jnienv():
global env_stacks, env_stack
cdef void pop_jnienv() nogil:
global env_stacks, env_stack, env_current
if env_stack == 0:
raise Exception('Jnius: cannot pop JNI env, already at 0')
with gil:
print('ERROR: Jnius cannot pop JNI env, already at 0')
return
env_stacks[env_stack] = NULL
env_stack -= 1
env_current = env_stacks[env_stack]

View File

@ -72,8 +72,9 @@ cdef class PythonJavaClass(object):
return py_method(*args)
cdef jobject invoke0(JNIEnv *j_env, jobject j_this, jobject j_proxy, jobject
cdef jobject py_invoke0(JNIEnv *j_env, jobject j_this, jobject j_proxy, jobject
j_method, jobjectArray args) with gil:
from .reflect import get_signature, Method
cdef jfieldID ptrField
cdef jlong jptr
@ -82,72 +83,79 @@ cdef jobject invoke0(JNIEnv *j_env, jobject j_this, jobject j_proxy, jobject
cdef LocalRef ref
cdef jobject j_arg
# get the python object
ptrField = j_env[0].GetFieldID(j_env,
j_env[0].GetObjectClass(j_env, j_this), "ptr", "J")
jptr = j_env[0].GetLongField(j_env, j_this, ptrField)
py_obj = <object><void *>jptr
# extract the method information
method = Method(noinstance=True)
ref = create_local_ref(j_env, j_method)
method.instanciate_from(create_local_ref(j_env, j_method))
ret_signature = get_signature(method.getReturnType())
args_signature = [get_signature(x) for x in method.getParameterTypes()]
# convert java argument to python object
# native java type are given with java.lang.*, even if the signature say
# it's a native type.
py_args = []
convert_signature = {
'Z': 'Ljava/lang/Boolean;',
'B': 'Ljava/lang/Byte;',
'C': 'Ljava/lang/Character;',
'S': 'Ljava/lang/Short;',
'I': 'Ljava/lang/Integer;',
'J': 'Ljava/lang/Long;',
'F': 'Ljava/lang/Float;',
'D': 'Ljava/lang/Double;'}
for index, arg_signature in enumerate(args_signature):
arg_signature = convert_signature.get(arg_signature, arg_signature)
j_arg = j_env[0].GetObjectArrayElement(j_env, args, index)
py_arg = convert_jobject_to_python(j_env, arg_signature, j_arg)
py_args.append(py_arg)
# really invoke the python method
name = method.getName()
ret = py_obj.invoke(method, *py_args)
# convert back to the return type
# use the populate_args(), but in the reverse way :)
t = ret_signature[:1]
# did python returned a "native" type ?
jtype = None
if ret_signature == 'Ljava/lang/Object;':
# generic object, try to manually convert it
tp = type(ret)
if tp == int:
jtype = 'J'
elif tp == float:
jtype = 'D'
elif tp == bool:
jtype = 'Z'
elif len(ret_signature) == 1:
jtype = ret_signature
try:
push_jnienv(j_env)
return convert_python_to_jobject(j_env, jtype or ret_signature, ret)
except Exception as e:
traceback.print_exc(e)
# get the python object
ptrField = j_env[0].GetFieldID(j_env,
j_env[0].GetObjectClass(j_env, j_this), "ptr", "J")
jptr = j_env[0].GetLongField(j_env, j_this, ptrField)
py_obj = <object><void *>jptr
# extract the method information
method = Method(noinstance=True)
ref = create_local_ref(j_env, j_method)
method.instanciate_from(create_local_ref(j_env, j_method))
ret_signature = get_signature(method.getReturnType())
args_signature = [get_signature(x) for x in method.getParameterTypes()]
# convert java argument to python object
# native java type are given with java.lang.*, even if the signature say
# it's a native type.
py_args = []
convert_signature = {
'Z': 'Ljava/lang/Boolean;',
'B': 'Ljava/lang/Byte;',
'C': 'Ljava/lang/Character;',
'S': 'Ljava/lang/Short;',
'I': 'Ljava/lang/Integer;',
'J': 'Ljava/lang/Long;',
'F': 'Ljava/lang/Float;',
'D': 'Ljava/lang/Double;'}
for index, arg_signature in enumerate(args_signature):
arg_signature = convert_signature.get(arg_signature, arg_signature)
j_arg = j_env[0].GetObjectArrayElement(j_env, args, index)
py_arg = convert_jobject_to_python(j_env, arg_signature, j_arg)
py_args.append(py_arg)
# really invoke the python method
name = method.getName()
ret = py_obj.invoke(method, *py_args)
# convert back to the return type
# use the populate_args(), but in the reverse way :)
t = ret_signature[:1]
# did python returned a "native" type ?
jtype = None
if ret_signature == 'Ljava/lang/Object;':
# generic object, try to manually convert it
tp = type(ret)
if tp == int:
jtype = 'J'
elif tp == float:
jtype = 'D'
elif tp == bool:
jtype = 'Z'
elif len(ret_signature) == 1:
jtype = ret_signature
cdef jobject invoke0(JNIEnv *j_env, jobject j_this, jobject j_proxy, jobject
j_method, jobjectArray args) nogil:
cdef jobject ret = NULL
push_jnienv(j_env)
with gil:
try:
return convert_python_to_jobject(j_env, jtype or ret_signature, ret)
ret = py_invoke0(j_env, j_this, j_proxy, j_method, args)
except Exception as e:
traceback.print_exc(e)
finally:
pop_jnienv()
pop_jnienv()
return ret
# now we need to create a proxy and pass it an invocation handler