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