[SOLVED]Sharing JObject among threads

Hey guys,

I've been trying to move a JObject from the main thread to the inner thread. I've tried implementing Channel, Arc and Mutex to share the object, but all of them gave me the same error.

error[E0277]: `*mut jni::sys::_jobject` cannot be sent between threads safely                                                                                                                                                                                           
--> src/lib.rs:88:19                                                                                                                                                                                                                                                  
|                                                                                                                                                                                                                                                                    
88 |     let handle2 = thread::spawn(move || {                                                                                                                                                                                                                          
|                   ^^^^^^^^^^^^^ `*mut jni::sys::_jobject` cannot be sent between threads safely                                                                                                                                                                    
|                                                                                                                                                                                                                                                                    
= help: within `jni::objects::JObject<'_>`, the trait `std::marker::Send` is not implemented for `*mut 
jni::sys::_jobject`                                                                                                                                           
= note: required because it appears within the type `jni::objects::JObject<'_>`                                                                                                                                                                                      
= note: required because of the requirements on the impl of `std::marker::Send` for 
`std::sync::Arc<jni::objects::JObject<'_>>`                                                                                                                                      
= note: required because it appears within the type `[closure@src/lib.rs:88:33: 106:6 JVM:jni::JavaVM, 
outerobj:std::sync::Arc<jni::objects::JObject<'_>>]`                                                                                                          
= note: required by `std::thread::spawn`

How do i fix this?? Thank you. :slight_smile:

1 Like

There're a couple of issues here with respect to moving JObject to another thread, one of which you've already hit:

  1. JObject is not std::marker::Send, as the compiler error mentions.
  2. JObject is actually JObject<'a>, so it has a lifetime that's associated with the JNIEnv it was obtained from. To move a value to another thread (using std's thread::spawn API), the value has to be 'static - i.e. have no references or only 'static references. This will not be the case here, I suppose, because you're not going to have a static JNIEnv.

Can you describe what you're trying to do in the bigger picture? Perhaps a GlobalRef is what you need instead of a JObject here.

3 Likes

I am trying to implement an Async callback from rust to java through jni.

Here is the code: https://github.com/Yoga07/java-jni-rust/tree/jni-error

Java_com_sample_jni_UserData_printUserData in lib.rs is the function receiving the callback interface as obj2. I am not able to share the obj2 between the main thread and the inner thread handle2.

Ok, so I think you'll want the GlobalRef I mentioned. AFAIK, a jobject passed as a JNI argument is only valid for the duration of the JNI call - the JVM is allowed (and most likely will) make it GC'able after the JNI call returns. If you want to then move this value to another thread, and thus make it (possibly) live beyond the JNI call itself, you need to inform the JVM about that (i.e. make it pinned) - this is what GlobalRef is for.

5 Likes

Thank you! GlobalRefs were the key :slight_smile: