Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade jni v0.21 #63

Open
wants to merge 4 commits into
base: rel-0.1
Choose a base branch
from
Open

upgrade jni v0.21 #63

wants to merge 4 commits into from

Conversation

flisky
Copy link

@flisky flisky commented Jan 16, 2024

jni v0.19 doesn't work for me.

I use tokio multi threads runtime, and our app is killed by system -

e.android.debug: java_vm_ext.cc:594] JNI DETECTED ERROR IN APPLICATION: a thread (tid 5917 is making JNI calls without being attached
e.android.debug: java_vm_ext.cc:594]     in call to PushLocalFrame

It turns out java_vm::get_env() goes null even after calling AttachCurrentThread in a few threads, while other threads are okay.
I have no idea. However, upgrade to jni v0.21.0 eliminates the problem for me.

fixes #22

@flisky flisky changed the title update jni v0.21 upgrade jni v0.21 Jan 16, 2024
@complexspaces
Copy link
Collaborator

Thanks for the PR! It looks like the changes here aren't currently compiling for Android. Once CI is passing I'm happy to take a look through this.

It turns out java_vm::get_env() goes null even after calling AttachCurrentThread in a few threads, while other threads are okay.
I have no idea. However, upgrade to jni v0.21.0 eliminates the problem for me.

That's very strange, and definitely something I'll look into on the side. We use this exact same JNI-abstraction inside 1Password's Android app (this code was copy/pasted out) and we've never seen this issue AFAICT. If you have any other JNI code in your app, it might be worth checking if its having a poor interaction with rustls-platform-verifier.

@flisky
Copy link
Author

flisky commented Jan 17, 2024

If you have any other JNI code in your app, it might be worth checking if its having a poor interaction with rustls-platform-verifier.

We're using mozilla/uniffi which uses JNA actually. We have to configure JNI environment mannually, and mozilla/uniffi-rs#1778 (comment) inspires us.

@complexspaces
Copy link
Collaborator

It would be great if jni could make another release before this merges. I don't want to push a very outdated version of windows-sys into everyone's dependency trees.

@@ -74,7 +74,7 @@ fn global() -> &'static Global {
/// nothing else in your application needs access the Android runtime.
///
/// Initialization must be done before any verification is attempted.
pub fn init_hosted(env: &JNIEnv, context: JObject) -> Result<(), JNIError> {
pub fn init_hosted(env: &mut JNIEnv, context: &JObject) -> Result<(), JNIError> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This is a breaking change and is probably going to require a new release.

cc @cpu

Comment on lines +141 to +142
pub(super) fn application_context(&self) -> GlobalRef {
self.context.clone()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Can this use as_obj instead for clarity? The result of application_context() is passed as an object.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid not. The context.env is a mutable reference, so other context fields must be full owned.

}

/// Load a class from the application class loader
///
/// This should be used instead of `JNIEnv::find_class` to ensure all classes
/// in the application can be found.
pub(super) fn load_class(&self, name: &str) -> Result<JClass<'a>, Error> {
pub(super) fn load_class(&mut self, name: &str) -> Result<JClass<'a>, Error> {
let loader = self.loader.clone();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This should be able to stay inline? Its .as_ref() call should produce the JObject that's needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's the same problem as the above question.


env.pop_local_frame(JObject::null())?;
unsafe { context.env.pop_local_frame(&JObject::null()) }?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have a // SAFETY: comment attached to it now, but I'm attempting to determine the soundness requirements. The documentation says local references become invalid, and I assume those are JVM references and not anything related to native code.

Given that we only return native Rust objects from f, I think we follow that rule correctly. If you happen to know, does that sound reasonable to you as well?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. Maybe it's better to declare T: 'static to make sure it's native rust object?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds like a good idea to me 👍

@complexspaces
Copy link
Collaborator

It turns out java_vm::get_env() goes null even after calling AttachCurrentThread in a few threads, while other threads are okay.
I have no idea. However, upgrade to jni v0.21.0 eliminates the problem for me.

We also use a multi-threaded Tokio runtime at work and haven't encountered this, so I am curious where the difference is.

@flisky Are you able to/do you mind sharing your Android context initialization code? While looking at the SO post in your linked GitHub comment, some things stood out to me as strange. Notably its unclear why they are wrangling the list of JVMs and current thread instead of passing an environment from Kotlin/Java and using JniEnv::get_java_vm (which calls GetJavaVM) instead. You can see an example of this here for this crate's tests.

@flisky
Copy link
Author

flisky commented Jan 23, 2024

I create a demo project for this, and you can take a look at https://github.com/flisky/jnidemo/blob/main/src/android.rs
Unfortunately, I cannot reproduce with that project. And I'll tweak it in next few days.

@flisky
Copy link
Author

flisky commented May 22, 2024

Hey @complexspaces, I'm back on this issue, and I finally find the root case.

During several days debugging, it turns out jna will also attach & detach thread if current thread is not attached.

We're usinguniffi callback to log tracing events, and the race happens:

  1. logging start (jna attached)
  2. verifier start (jni attached )
  3. logging finished (jna detached)
  4. verifying (jni calling).

Now, we're calling attach_current_thread_permanently manully when https://docs.rs/tokio/latest/tokio/runtime/struct.Builder.html#method.on_thread_start, and make sure jni's logging disabled.

Thanks this bug we find out something to optimized. Hope it help:)

(By the way, jna & jni can be mixed used, because java loads native library only once.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants