-
Hello, I'm migrating from 0.20.0 to 0.21.0 and the newly introduced API changes lead me to introduce more unsafe code to get things working and generally feel less ergonomic. In other words, I'm probably doing something wrong and I'd appreciate some assistance :-). For reference, here's a link to the code I'm discussing: https://github.com/questdb/questdb/blob/43a91d56453946ac4e4a70ec495306441f9cdafa/core/src/main/rust/questdb-jni/src/log.rs#L45 0.20.0 APII'm caching class and method lookups so these can be used across threads to call from Rust back into Java: I'm bridging Rust During initialization, I have some code that looks up a class and stores it as a let log_factory_class = env.find_class("io/questdb/log/LogFactory")?;
let log_factory_class = env.new_global_ref(log_factory_class)?; Later, from another thread, I'm using the looked-up information to call a static method. let j_name = jenv.new_string(name)
.expect("could not construct logger name string");
let j_name = jenv.auto_local(j_name);
let obj = jenv.call_static_method_unchecked(
call_state.log_factory_class.as_obj(),
call_state.log_factory_get_log_meth,
ReturnType::Object,
&[JValue::Object(j_name.as_obj()).into()])
.expect("io.questdb.log.LogFactory::getLog(String) call failed")
.l()
.expect("io.questdb.log.LogFactory::getLog(String) didn't return an object"); 0.21.0 APIThe let j_name = jenv.new_string(name)
.expect("could not construct logger name string");
let j_name = jenv.auto_local(j_name);
// Note: `call_state.log_factory_class.as_obj()` returns a `&JObject` whilst I need a JObject.
let j_class_obj: JObject = unsafe { JObject::from_raw(call_state.log_factory_class.as_obj().as_raw()) };
let j_class = JClass::from(j_class_obj);
let j_name_raw: jobject = j_name.as_raw();
let j_name_obj: JObject = unsafe { JObject::from_raw(j_name_raw) };
let j_name_value = JValue::Object(&j_name_obj);
let obj = unsafe { jenv.call_static_method_unchecked(
j_class,
call_state.log_factory_get_log_meth,
ReturnType::Object,
&[j_name_value.as_jni()])}
.expect("io.questdb.log.LogFactory::getLog(String) call failed")
.l()
.expect("io.questdb.log.LogFactory::getLog(String) didn't return an object"); This is significantly more complicated and requires My best guess is that I mis-understood how to use the API somewhere and I'm doing something wrong. Any advice here is wecome: Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 6 comments 9 replies
-
Have you tried directly passing a |
Beta Was this translation helpful? Give feedback.
-
Also, |
Beta Was this translation helpful? Give feedback.
-
Yeah, unfortunately we've repeatedly found that the JNI API has exposed unsafe APIs as if they are safe and 0.21 is just being a bit more honest about the safety. So actually these things were unsafe in 0.20 but it wasn't obvious. In some cases that does make things less ergonomic but I think it's probably better to know when things are unsafe than not. This way it's also clearer where more work is needed to provide a genuinely safe API.
One API that got some improvements in 0.21 was In general I think you should only really need to use |
Beta Was this translation helpful? Give feedback.
-
Thanks for the insightful details. |
Beta Was this translation helpful? Give feedback.
-
apologies for the delay replying here - I might be a bit busy to follow up on this closely for a while, sorry. I could have sworn we had some discussion around this before but wasn't able to dig it up. Making them If we're going to break the thread attachment APIs, I think I'd like to at least do a minimal exploration of my idea to introduce a stateful Failing if the thread is already attached initially seems like that could be a big pain but maybe in combination with an I can imagine that some crates might want to hide the details of JNI from their users (apart from possibly being initialized with a JVM pointer), and so then there could be various APIs that want to use JNI internally that haven't been directly passed a It's perhaps reasonable for that use case that it would require an On a related note also see this issue with thread attachment that was filed recently: #441 |
Beta Was this translation helpful? Give feedback.
-
Related to the "how to use JNI 0.21" discussion, I recently noticed that nikomatsakis is working on |
Beta Was this translation helpful? Give feedback.
Also,
let j_name_value = JValue::Object(j_name.as_ref())
should work because offn as_ref(&self) -> &JObject<'local>
.