From 83e0c7f75b4206701c1b9c62ebd4282f50f92a31 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Sat, 27 Jun 2020 14:49:38 +0200 Subject: Notify DBTNativeDownlink when its native JavaUplink -> JavaGlobalObj counterpart gets destructed DBTNativeDownlink.notifyDeleted() gets called from native destructor, so isValid and nativeInstance can be set accordingly. This resolves a periodic crash when GATTHandler flushes it's GATTServices and linked resources. Here the DBTGATTService.java's DBTNativeDownlink still holds the native instance handle and as it gets finalized after the native object, accessing it post mortem causes a SIGSEGV. --- java/direct_bt/tinyb/DBTNativeDownlink.java | 14 ++++++++++++-- java/jni/direct_bt/DBTDevice.cxx | 1 + java/jni/direct_bt/DBTManager.cxx | 2 +- java/jni/direct_bt/DBTNativeDownlink.cxx | 22 +++++++++++++++++++--- java/jni/direct_bt/helper_dbt.cxx | 11 +++++++++++ java/jni/direct_bt/helper_dbt.hpp | 7 +++++-- 6 files changed, 49 insertions(+), 8 deletions(-) (limited to 'java') diff --git a/java/direct_bt/tinyb/DBTNativeDownlink.java b/java/direct_bt/tinyb/DBTNativeDownlink.java index 5e4e3a71..b0b87057 100644 --- a/java/direct_bt/tinyb/DBTNativeDownlink.java +++ b/java/direct_bt/tinyb/DBTNativeDownlink.java @@ -64,11 +64,21 @@ public abstract class DBTNativeDownlink return; } isValid = false; - clearNativeJavaObject(nativeInstance); + deleteNativeJavaObject(nativeInstance); deleteImpl(); nativeInstance = 0; } + /** + * Called from native JavaUplink dtor -> JavaGlobalObj dtor, + * i.e. native instance destructed in native land. + */ + private synchronized void notifyDeleted() { + // System.err.println("***** notifyDeleted: "+getClass().getSimpleName()+": valid "+isValid+" -> false, handle 0x"+Long.toHexString(nativeInstance)+" -> null"); + isValid = false; + nativeInstance = 0; + } + /** * Deletes the native instance. *

@@ -79,5 +89,5 @@ public abstract class DBTNativeDownlink protected abstract void deleteImpl(); private native void initNativeJavaObject(final long nativeInstance); - private native void clearNativeJavaObject(final long nativeInstance); + private native void deleteNativeJavaObject(final long nativeInstance); } diff --git a/java/jni/direct_bt/DBTDevice.cxx b/java/jni/direct_bt/DBTDevice.cxx index 958fdba2..0a75b77c 100644 --- a/java/jni/direct_bt/DBTDevice.cxx +++ b/java/jni/direct_bt/DBTDevice.cxx @@ -252,6 +252,7 @@ void Java_direct_1bt_tinyb_DBTDevice_deleteImpl(JNIEnv *env, jobject obj) DBTDevice *device = getInstance(env, obj); device->remove(); // No delete: DBTDevice instance owned by DBTAdapter + // However, device->remove() might issue destruction } catch(...) { rethrow_and_raise_java_exception(env); } diff --git a/java/jni/direct_bt/DBTManager.cxx b/java/jni/direct_bt/DBTManager.cxx index 58478f3e..560a0f92 100644 --- a/java/jni/direct_bt/DBTManager.cxx +++ b/java/jni/direct_bt/DBTManager.cxx @@ -45,7 +45,7 @@ void Java_direct_1bt_tinyb_DBTManager_initImpl(JNIEnv *env, jobject obj, jboolea DBTManager *manager = &DBTManager::get(BTMode::BT_MODE_LE); // special: static singleton setInstance(env, obj, manager); java_exception_check_and_throw(env, E_FILE_LINE); - manager->setJavaObject( std::shared_ptr( new JavaGlobalObj(obj) ) ); + manager->setJavaObject( std::shared_ptr( new JavaGlobalObj(obj, nullptr) ) ); JavaGlobalObj::check(manager->getJavaObject(), E_FILE_LINE); DBG_PRINT("Java_direct_1bt_tinyb_DBTManager_init: Manager %s", manager->toString().c_str()); } catch(...) { diff --git a/java/jni/direct_bt/DBTNativeDownlink.cxx b/java/jni/direct_bt/DBTNativeDownlink.cxx index fdfacf5e..77afbd7f 100644 --- a/java/jni/direct_bt/DBTNativeDownlink.cxx +++ b/java/jni/direct_bt/DBTNativeDownlink.cxx @@ -40,7 +40,20 @@ void Java_direct_1bt_tinyb_DBTNativeDownlink_initNativeJavaObject(JNIEnv *env, j { try { JavaUplink *javaUplink = castInstance(nativeInstance); - std::shared_ptr jobjRef( new JavaGlobalObj(obj) ); + if( nullptr == javaUplink ) { + throw InternalError("NativeInstance JavaUplink is NULL", E_FILE_LINE); + } + jclass javaClazz = search_class(env, obj); + java_exception_check_and_throw(env, E_FILE_LINE); + if( nullptr == javaClazz ) { + throw InternalError("DBTNativeDownlink class not found", E_FILE_LINE); + } + jmethodID mNotifyDeleted = search_method(env, javaClazz, "notifyDeleted", "()V", false); + java_exception_check_and_throw(env, E_FILE_LINE); + if( nullptr == mNotifyDeleted ) { + throw InternalError("DBTNativeDownlink class has no notifyDeleted() method, for "+javaUplink->toString(), E_FILE_LINE); + } + std::shared_ptr jobjRef( new JavaGlobalObj(obj, mNotifyDeleted) ); javaUplink->setJavaObject( jobjRef ); JavaGlobalObj::check(javaUplink->getJavaObject(), E_FILE_LINE); DBG_PRINT("Java_direct_1bt_tinyb_DBTNativeDownlink_initNativeJavaObject %p -> %s", javaUplink, javaUplink->toString().c_str()); @@ -49,12 +62,15 @@ void Java_direct_1bt_tinyb_DBTNativeDownlink_initNativeJavaObject(JNIEnv *env, j } } -void Java_direct_1bt_tinyb_DBTNativeDownlink_clearNativeJavaObject(JNIEnv *env, jobject obj, jlong nativeInstance) +void Java_direct_1bt_tinyb_DBTNativeDownlink_deleteNativeJavaObject(JNIEnv *env, jobject obj, jlong nativeInstance) { (void)obj; try { JavaUplink *javaUplink = castInstance(nativeInstance); - DBG_PRINT("Java_direct_1bt_tinyb_DBTNativeDownlink_clearNativeJavaObject %p -> %s", javaUplink, javaUplink->toString().c_str()); + if( nullptr == javaUplink ) { + throw InternalError("NativeInstance JavaUplink is NULL", E_FILE_LINE); + } + DBG_PRINT("Java_direct_1bt_tinyb_DBTNativeDownlink_deleteNativeJavaObject %p -> %s", javaUplink, javaUplink->toString().c_str()); javaUplink->setJavaObject(nullptr); } catch(...) { rethrow_and_raise_java_exception(env); diff --git a/java/jni/direct_bt/helper_dbt.cxx b/java/jni/direct_bt/helper_dbt.cxx index b1690edf..7c0920db 100644 --- a/java/jni/direct_bt/helper_dbt.cxx +++ b/java/jni/direct_bt/helper_dbt.cxx @@ -67,3 +67,14 @@ jstring direct_bt::fromBDAddressTypeToJavaAddressType(JNIEnv *env, BDAddressType return from_string_to_jstring(env, jStringEmpty); } } + +JavaGlobalObj::~JavaGlobalObj() { + jobject obj = javaObjectRef.getObject(); + if( nullptr == obj || nullptr == mNotifyDeleted ) { + return; + } + JNIEnv *env = *jni_env; + env->CallVoidMethod(obj, mNotifyDeleted); + java_exception_check_and_throw(env, E_FILE_LINE); +} + diff --git a/java/jni/direct_bt/helper_dbt.hpp b/java/jni/direct_bt/helper_dbt.hpp index 40e2c609..d71104e7 100644 --- a/java/jni/direct_bt/helper_dbt.hpp +++ b/java/jni/direct_bt/helper_dbt.hpp @@ -59,6 +59,7 @@ namespace direct_bt { class JavaGlobalObj : public JavaAnonObj { private: JNIGlobalRef javaObjectRef; + jmethodID mNotifyDeleted; public: static inline void check(const std::shared_ptr & shref, const char* file, int line) { @@ -80,13 +81,16 @@ namespace direct_bt { } return true; } - JavaGlobalObj(jobject obj) : javaObjectRef(obj) { } + JavaGlobalObj(jobject obj, jmethodID mNotifyDeleted) + : javaObjectRef(obj), mNotifyDeleted(mNotifyDeleted) { } JavaGlobalObj(const JavaGlobalObj &o) noexcept = default; JavaGlobalObj(JavaGlobalObj &&o) noexcept = default; JavaGlobalObj& operator=(const JavaGlobalObj &o) noexcept = default; JavaGlobalObj& operator=(JavaGlobalObj &&o) noexcept = default; + virtual ~JavaGlobalObj(); + std::string toString() const override { const uint64_t ref = (uint64_t)(void*)javaObjectRef.getObject(); return "JavaGlobalObj["+uint64HexString(ref, true)+"]"; @@ -113,7 +117,6 @@ namespace direct_bt { } }; - jclass search_class(JNIEnv *env, JavaUplink &object); template -- cgit v1.2.3