From 0aaf92ffbb8dd19f903c0784ea4ea6584ad6d0ee Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Tue, 3 May 2016 17:05:27 +0200 Subject: [PATCH] HONEYCOMB-10: fix issues with FindClass in multithreaded environments Added jclass reference caching and updated JNI version to 1.8 Change-Id: Ie8dbbd4b91b90bf9e4e9a6148313e46056b0d67e Signed-off-by: Marek Gradzki --- vpp-api/java/jvpp/gen/jvpp_c_gen.py | 63 ++++++++++++++++++++++++++++++++----- vpp-api/java/jvpp/jvpp.c | 32 +++++++++---------- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/vpp-api/java/jvpp/gen/jvpp_c_gen.py b/vpp-api/java/jvpp/gen/jvpp_c_gen.py index c0efed81d30..e277976c720 100644 --- a/vpp-api/java/jvpp/gen/jvpp_c_gen.py +++ b/vpp-api/java/jvpp/gen/jvpp_c_gen.py @@ -20,7 +20,48 @@ from string import Template def is_manually_generated(f_name): return f_name in {'control_ping_reply'} -# TODO: cache class/method/field identifiers to achieve better performance + +class_reference_template = Template("""jclass ${ref_name}Class; +""") + +find_class_invocation_template = Template(""" + ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "org/openvpp/jvpp/dto/${class_name}")); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + return JNI_ERR; + }""") + +class_cache_template = Template(""" +$class_references +static int cache_class_references(JNIEnv* env) { + $find_class_invocations + return 0; +}""") + +def generate_class_cache(func_list): + class_references = [] + find_class_invocations = [] + for f in func_list: + c_name = f['name'] + class_name = util.underscore_to_camelcase_upper(c_name) + ref_name = util.underscore_to_camelcase(c_name) + + if not util.is_reply(class_name) or util.is_ignored(c_name) or util.is_notification(c_name): + # TODO handle notifications + continue + + class_references.append(class_reference_template.substitute( + ref_name=ref_name)) + + find_class_invocations.append(find_class_invocation_template.substitute( + ref_name=ref_name, + class_name=class_name)) + + return class_cache_template.substitute( + class_references="".join(class_references), find_class_invocations="".join(find_class_invocations)) + + +# TODO: cache method and field identifiers to achieve better performance # https://jira.fd.io/browse/HONEYCOMB-42 request_class_template = Template(""" jclass requestClass = (*env)->FindClass(env, "org/openvpp/jvpp/dto/${java_name_upper}");""") @@ -165,7 +206,7 @@ def generate_jni_impl(func_list, inputfile): dto_field_id_template = Template(""" - jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, dtoClass, "${java_name}", "${jni_signature}");""") + jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");""") default_dto_field_setter_template = Template(""" (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, mp->${c_name}); @@ -220,12 +261,10 @@ static void vl_api_${handler_name}_t_handler (vl_api_${handler_name}_t * mp) vppjni_main_t * jm = &vppjni_main; JNIEnv *env = jm->jenv; - jclass dtoClass = (*env)->FindClass(env, "org/openvpp/jvpp/dto/${dto_name}"); - - jmethodID constructor = (*env)->GetMethodID(env, dtoClass, "", "()V"); + jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "", "()V"); jmethodID callbackMethod = (*env)->GetMethodID(env, jm->callbackClass, "on${dto_name}", "(Lorg/openvpp/jvpp/dto/${dto_name};)V"); - jobject dto = (*env)->NewObject(env, dtoClass, constructor); + jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor); $dto_setters (*env)->CallVoidMethod(env, jm->callback, callbackMethod, dto); }""") @@ -235,6 +274,7 @@ def generate_msg_handlers(func_list, inputfile): for f in func_list: handler_name = f['name'] dto_name = util.underscore_to_camelcase_upper(handler_name) + ref_name = util.underscore_to_camelcase(handler_name) if is_manually_generated(handler_name) or not util.is_reply(dto_name) or util.is_ignored(handler_name) or util.is_notification(handler_name): # TODO handle notifications @@ -253,6 +293,7 @@ def generate_msg_handlers(func_list, inputfile): dto_setters += dto_field_id_template.substitute( java_name=java_field_name, + class_ref_name=ref_name, jni_signature=jni_signature) dto_setter_template = dto_field_setter_templates[c_type] @@ -268,6 +309,7 @@ def generate_msg_handlers(func_list, inputfile): api_data=util.api_message_to_javadoc(f), handler_name=handler_name, dto_name=dto_name, + class_ref_name=ref_name, dto_setters=dto_setters)) return "\n".join(handlers) @@ -293,13 +335,16 @@ def generate_handler_registration(func_list): return "".join(handler_registration) -jvpp_c_template = Template(""" -/** + +jvpp_c_template = Template("""/** * This file contains JNI bindings for jvpp Java API. * It was generated by jvpp_c_gen.py based on $inputfile * (python representation of vpe.api generated by vppapigen). */ +// JAVA class reference cache +$class_cache + // JNI bindings $jni_implementations @@ -314,6 +359,7 @@ def generate_jvpp(func_list, inputfile): """ Generates jvpp C file """ print "Generating jvpp C" + class_cache = generate_class_cache(func_list) jni_impl = generate_jni_impl(func_list, inputfile) msg_handlers = generate_msg_handlers(func_list, inputfile) handler_registration = generate_handler_registration(func_list) @@ -321,6 +367,7 @@ def generate_jvpp(func_list, inputfile): jvpp_c_file = open("jvpp_gen.h", 'w') jvpp_c_file.write(jvpp_c_template.substitute( inputfile=inputfile, + class_cache=class_cache, jni_implementations=jni_impl, msg_handlers=msg_handlers, handler_registration=handler_registration)) diff --git a/vpp-api/java/jvpp/jvpp.c b/vpp-api/java/jvpp/jvpp.c index f1e23dc7c8a..6cc05530d37 100644 --- a/vpp-api/java/jvpp/jvpp.c +++ b/vpp-api/java/jvpp/jvpp.c @@ -76,7 +76,7 @@ static void cleanup_rx_thread(void *arg) vppjni_lock (jm, 99); - int getEnvStat = (*jm->jvm)->GetEnv(jm->jvm, (void **)&(jm->jenv), JNI_VERSION_1_6); + int getEnvStat = (*jm->jvm)->GetEnv(jm->jvm, (void **)&(jm->jenv), JNI_VERSION_1_8); if (getEnvStat == JNI_EVERSION) { clib_warning ("Unsupported JNI version\n"); jm->retval = VNET_API_ERROR_UNSUPPORTED_JNI_VERSION; @@ -170,7 +170,7 @@ static void vl_api_control_ping_reply_t_handler char was_thread_connected = 0; // attach to java thread if not attached - int getEnvStat = (*jm->jvm)->GetEnv(jm->jvm, (void **)&(jm->jenv), JNI_VERSION_1_6); + int getEnvStat = (*jm->jvm)->GetEnv(jm->jvm, (void **)&(jm->jenv), JNI_VERSION_1_8); if (getEnvStat == JNI_EDETACHED) { if ((*jm->jvm)->AttachCurrentThread(jm->jvm, (void **)&(jm->jenv), NULL) != 0) { clib_warning("Failed to attach thread\n"); @@ -192,26 +192,21 @@ static void vl_api_control_ping_reply_t_handler if (was_thread_connected == 0) { JNIEnv *env = jm->jenv; - jclass dtoClass = (*env)->FindClass(env, "org/openvpp/jvpp/dto/ControlPingReply"); - - jmethodID constructor = (*env)->GetMethodID(env, dtoClass, "", "()V"); + jmethodID constructor = (*env)->GetMethodID(env, controlPingReplyClass, "", "()V"); jmethodID callbackMethod = (*env)->GetMethodID(env, jm->callbackClass, "onControlPingReply", "(Lorg/openvpp/jvpp/dto/ControlPingReply;)V"); - jobject dto = (*env)->NewObject(env, dtoClass, constructor); - - - printf("vl_api_control_ping_reply_t_handler ctx=%d\n", clib_net_to_host_u32(mp->context)); + jobject dto = (*env)->NewObject(env, controlPingReplyClass, constructor); - jfieldID contextFieldId = (*env)->GetFieldID(env, dtoClass, "context", "I"); + jfieldID contextFieldId = (*env)->GetFieldID(env, controlPingReplyClass, "context", "I"); (*env)->SetIntField(env, dto, contextFieldId, clib_net_to_host_u32(mp->context)); - jfieldID retvalFieldId = (*env)->GetFieldID(env, dtoClass, "retval", "I"); + jfieldID retvalFieldId = (*env)->GetFieldID(env, controlPingReplyClass, "retval", "I"); (*env)->SetIntField(env, dto, retvalFieldId, clib_net_to_host_u32(mp->retval)); - jfieldID clientIndexFieldId = (*env)->GetFieldID(env, dtoClass, "clientIndex", "I"); + jfieldID clientIndexFieldId = (*env)->GetFieldID(env, controlPingReplyClass, "clientIndex", "I"); (*env)->SetIntField(env, dto, clientIndexFieldId, clib_net_to_host_u32(mp->client_index)); - jfieldID vpePidFieldId = (*env)->GetFieldID(env, dtoClass, "vpePid", "I"); + jfieldID vpePidFieldId = (*env)->GetFieldID(env, controlPingReplyClass, "vpePid", "I"); (*env)->SetIntField(env, dto, vpePidFieldId, clib_net_to_host_u32(mp->vpe_pid)); (*env)->CallVoidMethod(env, jm->callback, callbackMethod, dto); @@ -221,22 +216,25 @@ static void vl_api_control_ping_reply_t_handler jm->result_ready = 1; } - jint JNI_OnLoad(JavaVM *vm, void *reserved) { vppjni_main_t * jm = &vppjni_main; JNIEnv* env; - if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) { + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) { + return JNI_EVERSION; + } + + if (cache_class_references(env) != 0) { return JNI_ERR; } jm->jvm = vm; - return JNI_VERSION_1_6; + return JNI_VERSION_1_8; } void JNI_OnUnload(JavaVM *vm, void *reserved) { vppjni_main_t * jm = &vppjni_main; JNIEnv* env; - if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) { + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) { return; } -- 2.16.6