1 use std::sync::Arc; 2 3 use log::{debug, warn}; 4 5 use crate::{ 6 errors::Result, 7 objects::{GlobalRef, JObject}, 8 sys, JNIEnv, JavaVM, 9 }; 10 11 // Note: `WeakRef` must not implement `Into<JObject>`! If it did, then it would be possible to 12 // wrap it in `AutoLocal`, which would cause undefined behavior upon drop as a result of calling 13 // the wrong JNI function to delete the reference. 14 15 /// A *weak* global JVM reference. These are global in scope like 16 /// [`GlobalRef`], and may outlive the `JNIEnv` they came from, but are 17 /// *not* guaranteed to not get collected until released. 18 /// 19 /// `WeakRef` can be cloned to use _the same_ weak reference in different 20 /// contexts. If you want to create yet another weak ref to the same java object, call 21 /// [`WeakRef::clone_in_jvm`]. 22 /// 23 /// Underlying weak reference will be dropped, when the last instance 24 /// of `WeakRef` leaves its scope. 25 /// 26 /// It is _recommended_ that a native thread that drops the weak reference is attached 27 /// to the Java thread (i.e., has an instance of `JNIEnv`). If the native thread is *not* attached, 28 /// the `WeakRef#drop` will print a warning and implicitly `attach` and `detach` it, which 29 /// significantly affects performance. 30 31 #[derive(Clone)] 32 pub struct WeakRef { 33 inner: Arc<WeakRefGuard>, 34 } 35 36 struct WeakRefGuard { 37 raw: sys::jweak, 38 vm: JavaVM, 39 } 40 41 unsafe impl Send for WeakRef {} 42 unsafe impl Sync for WeakRef {} 43 44 impl WeakRef { 45 /// Creates a new wrapper for a global reference. 46 /// 47 /// # Safety 48 /// 49 /// Expects a valid raw weak global reference that should be created with `NewWeakGlobalRef` 50 /// JNI function. from_raw(vm: JavaVM, raw: sys::jweak) -> Self51 pub(crate) unsafe fn from_raw(vm: JavaVM, raw: sys::jweak) -> Self { 52 WeakRef { 53 inner: Arc::new(WeakRefGuard { raw, vm }), 54 } 55 } 56 57 /// Returns the raw JNI weak reference. as_raw(&self) -> sys::jweak58 pub fn as_raw(&self) -> sys::jweak { 59 self.inner.raw 60 } 61 62 /// Creates a new local reference to this object. 63 /// 64 /// This object may have already been garbage collected by the time this method is called. If 65 /// so, this method returns `Ok(None)`. Otherwise, it returns `Ok(Some(r))` where `r` is the 66 /// new local reference. 67 /// 68 /// If this method returns `Ok(Some(r))`, it is guaranteed that the object will not be garbage 69 /// collected at least until `r` is deleted or becomes invalid. upgrade_local<'local>(&self, env: &JNIEnv<'local>) -> Result<Option<JObject<'local>>>70 pub fn upgrade_local<'local>(&self, env: &JNIEnv<'local>) -> Result<Option<JObject<'local>>> { 71 let r = env.new_local_ref(unsafe { JObject::from_raw(self.as_raw()) })?; 72 73 // Per JNI spec, `NewLocalRef` will return a null pointer if the object was GC'd. 74 if r.is_null() { 75 Ok(None) 76 } else { 77 Ok(Some(r)) 78 } 79 } 80 81 /// Creates a new strong global reference to this object. 82 /// 83 /// This object may have already been garbage collected by the time this method is called. If 84 /// so, this method returns `Ok(None)`. Otherwise, it returns `Ok(Some(r))` where `r` is the 85 /// new strong global reference. 86 /// 87 /// If this method returns `Ok(Some(r))`, it is guaranteed that the object will not be garbage 88 /// collected at least until `r` is dropped. upgrade_global(&self, env: &JNIEnv) -> Result<Option<GlobalRef>>89 pub fn upgrade_global(&self, env: &JNIEnv) -> Result<Option<GlobalRef>> { 90 let r = env.new_global_ref(unsafe { JObject::from_raw(self.as_raw()) })?; 91 92 // Unlike `NewLocalRef`, the JNI spec does *not* guarantee that `NewGlobalRef` will return a 93 // null pointer if the object was GC'd, so we'll have to check. 94 if env.is_same_object(&r, JObject::null())? { 95 Ok(None) 96 } else { 97 Ok(Some(r)) 98 } 99 } 100 101 /// Checks if the object referred to by this `WeakRef` has been garbage collected. 102 /// 103 /// Note that garbage collection can happen at any moment, so a return of `Ok(true)` from this 104 /// method does not guarantee that [`WeakRef::upgrade_local`] or [`WeakRef::upgrade_global`] 105 /// will succeed. 106 /// 107 /// This is equivalent to 108 /// <code>self.[is_same_object][WeakRef::is_same_object](env, [JObject::null]\())</code>. is_garbage_collected(&self, env: &JNIEnv) -> Result<bool>109 pub fn is_garbage_collected(&self, env: &JNIEnv) -> Result<bool> { 110 self.is_same_object(env, JObject::null()) 111 } 112 113 // The following methods are wrappers around those `JNIEnv` methods that make sense for a weak 114 // reference. These methods exist because they use `JObject::from_raw` on the raw pointer of a 115 // weak reference. Although this usage is sound, it is `unsafe`. It's also confusing because 116 // `JObject` normally represents a strong reference. 117 118 /// Returns true if this weak reference refers to the given object. Otherwise returns false. 119 /// 120 /// If `object` is [null][JObject::null], then this method is equivalent to 121 /// [`WeakRef::is_garbage_collected`]: it returns true if the object referred to by this 122 /// `WeakRef` has been garbage collected, or false if the object has not yet been garbage 123 /// collected. is_same_object<'local, O>(&self, env: &JNIEnv<'local>, object: O) -> Result<bool> where O: AsRef<JObject<'local>>,124 pub fn is_same_object<'local, O>(&self, env: &JNIEnv<'local>, object: O) -> Result<bool> 125 where 126 O: AsRef<JObject<'local>>, 127 { 128 env.is_same_object(unsafe { JObject::from_raw(self.as_raw()) }, object) 129 } 130 131 /// Returns true if this weak reference refers to the same object as another weak reference. 132 /// Otherwise returns false. 133 /// 134 /// This method will also return true if both weak references refer to an object that has been 135 /// garbage collected. is_weak_ref_to_same_object(&self, env: &JNIEnv, other: &WeakRef) -> Result<bool>136 pub fn is_weak_ref_to_same_object(&self, env: &JNIEnv, other: &WeakRef) -> Result<bool> { 137 self.is_same_object(env, unsafe { JObject::from_raw(other.as_raw()) }) 138 } 139 140 /// Creates a new weak reference to the same object that this one refers to. 141 /// 142 /// `WeakRef` implements [`Clone`], which should normally be used whenever a new `WeakRef` to 143 /// the same object is needed. However, that only increments an internal reference count and 144 /// does not actually create a new weak reference in the JVM. If you specifically need to have 145 /// the JVM create a new weak reference, use this method instead of `Clone`. 146 /// 147 /// This method returns `Ok(None)` if the object has already been garbage collected. clone_in_jvm(&self, env: &JNIEnv) -> Result<Option<WeakRef>>148 pub fn clone_in_jvm(&self, env: &JNIEnv) -> Result<Option<WeakRef>> { 149 env.new_weak_ref(unsafe { JObject::from_raw(self.as_raw()) }) 150 } 151 } 152 153 impl Drop for WeakRefGuard { drop(&mut self)154 fn drop(&mut self) { 155 fn drop_impl(env: &JNIEnv, raw: sys::jweak) -> Result<()> { 156 let internal = env.get_native_interface(); 157 // This method is safe to call in case of pending exceptions (see chapter 2 of the spec) 158 jni_unchecked!(internal, DeleteWeakGlobalRef, raw); 159 Ok(()) 160 } 161 162 let res = match self.vm.get_env() { 163 Ok(env) => drop_impl(&env, self.raw), 164 Err(_) => { 165 warn!("Dropping a WeakRef in a detached thread. Fix your code if this message appears frequently (see the WeakRef docs)."); 166 self.vm 167 .attach_current_thread() 168 .and_then(|env| drop_impl(&env, self.raw)) 169 } 170 }; 171 172 if let Err(err) = res { 173 debug!("error dropping weak ref: {:#?}", err); 174 } 175 } 176 } 177