Эх сурвалжийг харах

app: do proper JVM thread mgmt. previously we call AttachThread() everywhere which is wrong, it should be called once per thread and then DetachThread() to cleanup. use thread local storage + Drop to make a thread guard for this.

jkds 7 сар өмнө
parent
commit
a9e1f04a9f

+ 1 - 1
bin/app/Makefile

@@ -22,7 +22,7 @@ DEBUG_FEATURES = --features=enable-filelog,enable-plugins
 
 #DEV_FEATURES = --features=enable-filelog,enable-netdebug,emulate-android
 #DEV_FEATURES = --features=enable-filelog,enable-netdebug,enable-plugins
-DEV_FEATURES = --features=schema-test
+DEV_FEATURES = --no-default-features --features=schema-app
 
 default: build-release
 	./darkfi-app

+ 7 - 5
bin/app/src/android/mod.rs

@@ -22,13 +22,15 @@ use std::{collections::HashMap, path::PathBuf, sync::LazyLock};
 
 pub mod insets;
 pub mod textinput;
-mod util;
+pub(self) mod util;
 pub mod vid;
 
+use util::get_jni_env;
+
 macro_rules! call_mainactivity_int_method {
     ($method:expr, $sig:expr $(, $args:expr)*) => {{
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             ndk_utils::call_int_method!(env, android::ACTIVITY, $method, $sig $(, $args)*)
         }
     }};
@@ -36,7 +38,7 @@ macro_rules! call_mainactivity_int_method {
 macro_rules! call_mainactivity_str_method {
     ($method:expr) => {{
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             let text = ndk_utils::call_object_method!(
                 env,
                 android::ACTIVITY,
@@ -50,7 +52,7 @@ macro_rules! call_mainactivity_str_method {
 macro_rules! call_mainactivity_float_method {
     ($method:expr) => {{
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             ndk_utils::call_method!(CallFloatMethod, env, android::ACTIVITY, $method, "()F")
         }
     }};
@@ -58,7 +60,7 @@ macro_rules! call_mainactivity_float_method {
 macro_rules! call_mainactivity_bool_method {
     ($method:expr) => {{
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             ndk_utils::call_method!(CallBooleanMethod, env, android::ACTIVITY, $method, "()Z") !=
                 0u8
         }

+ 11 - 11
bin/app/src/android/textinput/gametextinput.rs

@@ -23,7 +23,7 @@ use std::{
     sync::{Arc, OnceLock},
 };
 
-use super::{AndroidTextInputState, SharedStatePtr};
+use super::{super::util::get_jni_env, AndroidTextInputState, SharedStatePtr};
 
 macro_rules! t { ($($arg:tt)*) => { trace!(target: "android::textinput::gametextinput", $($arg)*); } }
 macro_rules! w { ($($arg:tt)*) => { warn!(target: "android::textinput::gametextinput", $($arg)*); } }
@@ -57,7 +57,7 @@ pub struct GameTextInput {
 impl GameTextInput {
     pub fn new() -> Self {
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
 
             let find_class = (**env).FindClass.unwrap();
 
@@ -186,7 +186,7 @@ impl GameTextInput {
             return
         };
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             let jstate = self.state_to_java(state);
             call_void_method!(env, input_connection, "setState", "(Ltextinput/State;)V", jstate);
 
@@ -201,7 +201,7 @@ impl GameTextInput {
             return Err(())
         };
         let is_success = unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             call_bool_method!(env, input_connection, "setSelection", "(II)Z", start, end)
         };
         if is_success == 0u8 {
@@ -212,7 +212,7 @@ impl GameTextInput {
 
     pub fn set_input_connection(&self, input_connection: ndk_sys::jobject) {
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             let mut ic = self.input_connection.write();
             if let Some(old_ref) = *ic {
                 let delete_global_ref = (**env).DeleteGlobalRef.unwrap();
@@ -242,7 +242,7 @@ impl GameTextInput {
             return
         };
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             let call_void_method = (**env).CallVoidMethod.unwrap();
             call_void_method(
                 env,
@@ -260,7 +260,7 @@ impl GameTextInput {
             return
         };
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             let call_void_method = (**env).CallVoidMethod.unwrap();
             call_void_method(
                 env,
@@ -278,7 +278,7 @@ impl GameTextInput {
             return
         };
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             let call_void_method = (**env).CallVoidMethod.unwrap();
             call_void_method(env, input_connection, self.restart_input_method);
         }
@@ -286,7 +286,7 @@ impl GameTextInput {
 
     fn state_to_java(&self, state: &AndroidTextInputState) -> ndk_sys::jobject {
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             let new_string_utf = (**env).NewStringUTF.unwrap();
             let text_str = CString::new(state.text.as_str()).unwrap();
             let jtext = new_string_utf(env, text_str.as_ptr());
@@ -317,7 +317,7 @@ impl GameTextInput {
 
     fn state_from_java(&self, event_state: ndk_sys::jobject) -> AndroidTextInputState {
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             let get_object_field = (**env).GetObjectField.unwrap();
             let jtext =
                 get_object_field(env, event_state, self.state_class_info.text) as ndk_sys::jstring;
@@ -355,7 +355,7 @@ impl GameTextInput {
 impl Drop for GameTextInput {
     fn drop(&mut self) {
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             let delete_global_ref = (**env).DeleteGlobalRef.unwrap();
             if self.input_connection_class != std::ptr::null_mut() {
                 delete_global_ref(env, self.input_connection_class);

+ 51 - 1
bin/app/src/android/util.rs

@@ -16,7 +16,57 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use miniquad::native::android::ndk_sys;
+use miniquad::native::android::{self, ndk_sys};
+use std::cell::RefCell;
+
+thread_local! {
+    static JNI_ENV: RefCell<JniEnvHolder> = RefCell::new(JniEnvHolder {
+        env: std::ptr::null_mut(),
+        vm: std::ptr::null_mut(),
+    });
+}
+
+struct JniEnvHolder {
+    env: *mut ndk_sys::JNIEnv,
+    vm: *mut ndk_sys::JavaVM,
+}
+
+impl Drop for JniEnvHolder {
+    fn drop(&mut self) {
+        assert!(!self.env.is_null());
+        unsafe {
+            let detach_current_thread = (**self.vm).DetachCurrentThread.unwrap();
+            let _ = detach_current_thread(self.vm);
+        }
+    }
+}
+
+/// Get the JNIEnv for the current thread, attaching if necessary.
+/// The returned pointer is cached per-thread and automatically detached
+/// when the thread exits.
+pub unsafe fn get_jni_env() -> *mut ndk_sys::JNIEnv {
+    JNI_ENV.with(|holder| {
+        let mut holder = holder.borrow_mut();
+        if !holder.env.is_null() {
+            return holder.env;
+        }
+
+        // Call miniquad's attach_jni_env to get the env
+        let env = android::attach_jni_env();
+        assert!(!env.is_null());
+
+        // Retrieve the JavaVM from the JNIEnv
+        let get_java_vm = (**env).GetJavaVM.unwrap();
+        let mut vm: *mut ndk_sys::JavaVM = std::ptr::null_mut();
+        let res = get_java_vm(env, &mut vm);
+        assert!(res == 0);
+        assert!(!vm.is_null());
+
+        holder.env = env;
+        holder.vm = vm;
+        env
+    })
+}
 
 /// Check for pending Java exceptions and log them
 ///

+ 6 - 4
bin/app/src/android/vid.rs

@@ -25,6 +25,8 @@ use std::{
     sync::{mpsc, LazyLock},
 };
 
+use super::util::get_jni_env;
+
 pub struct DecodedFrame {
     pub width: usize,
     pub height: usize,
@@ -113,7 +115,7 @@ pub struct VideoDecoderHandle {
 impl Drop for VideoDecoderHandle {
     fn drop(&mut self) {
         unsafe {
-            let env = android::attach_jni_env();
+            let env = get_jni_env();
             let delete_local_ref = (**env).DeleteLocalRef.unwrap();
             delete_local_ref(env, self.obj);
         }
@@ -122,7 +124,7 @@ impl Drop for VideoDecoderHandle {
 
 pub fn videodecoder_init(path: &str) -> Option<VideoDecoderHandle> {
     unsafe {
-        let env = android::attach_jni_env();
+        let env = get_jni_env();
 
         let decoder_obj = ndk_utils::call_object_method!(
             env,
@@ -156,14 +158,14 @@ pub fn videodecoder_init(path: &str) -> Option<VideoDecoderHandle> {
 
 pub fn videodecoder_set_id(decoder_obj: ndk_sys::jobject, id: usize) {
     unsafe {
-        let env = android::attach_jni_env();
+        let env = get_jni_env();
         ndk_utils::call_void_method!(env, decoder_obj, "setDecoderId", "(I)V", id as i32);
     }
 }
 
 pub fn videodecoder_decode_all(decoder_obj: ndk_sys::jobject) -> i32 {
     unsafe {
-        let env = android::attach_jni_env();
+        let env = get_jni_env();
         ndk_utils::call_int_method!(env, decoder_obj, "decodeAll", "()I")
     }
 }

+ 13 - 4
bin/app/src/app/schema/test.rs

@@ -18,6 +18,7 @@
 
 use sled_overlay::sled;
 
+use super::chat::populate_tree;
 use crate::{
     app::{
         node::{
@@ -33,20 +34,28 @@ use crate::{
     ui::{BaseEdit, BaseEditType, ChatView, Layer, Text, VectorArt, VectorShape, Video},
     util::i18n::I18nBabelFish,
 };
-use super::chat::populate_tree;
 
 const LIGHTMODE: bool = false;
 
 #[cfg(target_os = "android")]
 mod ui_consts {
-    pub const CHATDB_PATH: &str = "/data/data/darkfi.app/chatdb/";
+    use crate::android::get_appdata_path;
+    use std::path::PathBuf;
+
+    pub fn get_chatdb_path() -> PathBuf {
+        get_appdata_path().join("chatdb")
+    }
     //pub const KING_PATH: &str = "king.png";
     pub const VID_PATH: &str = "forest_720x1280.mp4";
 }
 
 #[cfg(not(target_os = "android"))]
 mod ui_consts {
-    pub const CHATDB_PATH: &str = "chatdb";
+    use std::path::PathBuf;
+
+    pub fn get_chatdb_path() -> PathBuf {
+        "chatdb"
+    }
     //pub const KING_PATH: &str = "assets/king.png";
     pub const VID_PATH: &str = "assets/forest_1920x1080.ivf";
 }
@@ -342,7 +351,7 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
         prop.set_f32(atom, Role::App, 3, 1.).unwrap();
     }
 
-    let db = sled::open(CHATDB_PATH).expect("cannot open sleddb");
+    let db = sled::open(get_chatdb_path()).expect("cannot open sleddb");
     let chat_tree = db.open_tree(b"chat").unwrap();
     if chat_tree.is_empty() {
         populate_tree(&chat_tree);