use std::sync::Arc;
use log::{debug, warn};
use crate::{
errors::Result,
objects::{GlobalRef, JObject},
sys, JNIEnv, JavaVM,
};
#[derive(Clone)]
pub struct WeakRef {
inner: Arc<WeakRefGuard>,
}
struct WeakRefGuard {
raw: sys::jweak,
vm: JavaVM,
}
unsafe impl Send for WeakRef {}
unsafe impl Sync for WeakRef {}
impl WeakRef {
pub(crate) unsafe fn from_raw(vm: JavaVM, raw: sys::jweak) -> Self {
WeakRef {
inner: Arc::new(WeakRefGuard { raw, vm }),
}
}
pub fn as_raw(&self) -> sys::jweak {
self.inner.raw
}
pub fn upgrade_local<'local>(&self, env: &JNIEnv<'local>) -> Result<Option<JObject<'local>>> {
let r = env.new_local_ref(unsafe { JObject::from_raw(self.as_raw()) })?;
if r.is_null() {
Ok(None)
} else {
Ok(Some(r))
}
}
pub fn upgrade_global(&self, env: &JNIEnv) -> Result<Option<GlobalRef>> {
let r = env.new_global_ref(unsafe { JObject::from_raw(self.as_raw()) })?;
if env.is_same_object(&r, JObject::null())? {
Ok(None)
} else {
Ok(Some(r))
}
}
pub fn is_garbage_collected(&self, env: &JNIEnv) -> Result<bool> {
self.is_same_object(env, JObject::null())
}
pub fn is_same_object<'local, O>(&self, env: &JNIEnv<'local>, object: O) -> Result<bool>
where
O: AsRef<JObject<'local>>,
{
env.is_same_object(unsafe { JObject::from_raw(self.as_raw()) }, object)
}
pub fn is_weak_ref_to_same_object(&self, env: &JNIEnv, other: &WeakRef) -> Result<bool> {
self.is_same_object(env, unsafe { JObject::from_raw(other.as_raw()) })
}
pub fn clone_in_jvm(&self, env: &JNIEnv) -> Result<Option<WeakRef>> {
env.new_weak_ref(unsafe { JObject::from_raw(self.as_raw()) })
}
}
impl Drop for WeakRefGuard {
fn drop(&mut self) {
fn drop_impl(env: &JNIEnv, raw: sys::jweak) -> Result<()> {
let internal = env.get_native_interface();
jni_unchecked!(internal, DeleteWeakGlobalRef, raw);
Ok(())
}
let res = match self.vm.get_env() {
Ok(env) => drop_impl(&env, self.raw),
Err(_) => {
warn!("Dropping a WeakRef in a detached thread. Fix your code if this message appears frequently (see the WeakRef docs).");
self.vm
.attach_current_thread()
.and_then(|env| drop_impl(&env, self.raw))
}
};
if let Err(err) = res {
debug!("error dropping weak ref: {:#?}", err);
}
}
}