• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::{mem, ops::Deref, sync::Arc};
2 
3 use log::{debug, warn};
4 
5 use crate::{errors::Result, objects::JObject, sys, JNIEnv, JavaVM};
6 
7 // Note: `GlobalRef` must not implement `Into<JObject>`! If it did, then it would be possible to
8 // wrap it in `AutoLocal`, which would cause undefined behavior upon drop as a result of calling
9 // the wrong JNI function to delete the reference.
10 
11 /// A global JVM reference. These are "pinned" by the garbage collector and are
12 /// guaranteed to not get collected until released. Thus, this is allowed to
13 /// outlive the `JNIEnv` that it came from and can be used in other threads.
14 ///
15 /// `GlobalRef` can be cloned to use _the same_ global reference in different
16 /// contexts. If you want to create yet another global ref to the same java object
17 /// you may call `JNIEnv#new_global_ref` just like you do when create `GlobalRef`
18 /// from a local reference.
19 ///
20 /// Underlying global reference will be dropped, when the last instance
21 /// of `GlobalRef` leaves its scope.
22 ///
23 /// It is _recommended_ that a native thread that drops the global reference is attached
24 /// to the Java thread (i.e., has an instance of `JNIEnv`). If the native thread is *not* attached,
25 /// the `GlobalRef#drop` will print a warning and implicitly `attach` and `detach` it, which
26 /// significantly affects performance.
27 
28 #[derive(Clone, Debug)]
29 pub struct GlobalRef {
30     inner: Arc<GlobalRefGuard>,
31 }
32 
33 #[derive(Debug)]
34 struct GlobalRefGuard {
35     obj: JObject<'static>,
36     vm: JavaVM,
37 }
38 
39 impl AsRef<GlobalRef> for GlobalRef {
as_ref(&self) -> &GlobalRef40     fn as_ref(&self) -> &GlobalRef {
41         self
42     }
43 }
44 
45 impl AsRef<JObject<'static>> for GlobalRef {
as_ref(&self) -> &JObject<'static>46     fn as_ref(&self) -> &JObject<'static> {
47         self
48     }
49 }
50 
51 impl Deref for GlobalRef {
52     type Target = JObject<'static>;
53 
deref(&self) -> &Self::Target54     fn deref(&self) -> &Self::Target {
55         &self.inner.obj
56     }
57 }
58 
59 impl GlobalRef {
60     /// Creates a new wrapper for a global reference.
61     ///
62     /// # Safety
63     ///
64     /// Expects a valid raw global reference that should be created with `NewGlobalRef` JNI function.
from_raw(vm: JavaVM, raw_global_ref: sys::jobject) -> Self65     pub(crate) unsafe fn from_raw(vm: JavaVM, raw_global_ref: sys::jobject) -> Self {
66         GlobalRef {
67             inner: Arc::new(GlobalRefGuard::from_raw(vm, raw_global_ref)),
68         }
69     }
70 
71     /// Get the object from the global ref
72     ///
73     /// This borrows the ref and prevents it from being dropped as long as the
74     /// JObject sticks around.
as_obj(&self) -> &JObject<'static>75     pub fn as_obj(&self) -> &JObject<'static> {
76         self.as_ref()
77     }
78 }
79 
80 impl GlobalRefGuard {
81     /// Creates a new global reference guard. This assumes that `NewGlobalRef`
82     /// has already been called.
from_raw(vm: JavaVM, obj: sys::jobject) -> Self83     unsafe fn from_raw(vm: JavaVM, obj: sys::jobject) -> Self {
84         GlobalRefGuard {
85             obj: JObject::from_raw(obj),
86             vm,
87         }
88     }
89 }
90 
91 impl Drop for GlobalRefGuard {
drop(&mut self)92     fn drop(&mut self) {
93         let raw: sys::jobject = mem::take(&mut self.obj).into_raw();
94 
95         let drop_impl = |env: &JNIEnv| -> Result<()> {
96             let internal = env.get_native_interface();
97             // This method is safe to call in case of pending exceptions (see chapter 2 of the spec)
98             jni_unchecked!(internal, DeleteGlobalRef, raw);
99             Ok(())
100         };
101 
102         let res = match self.vm.get_env() {
103             Ok(env) => drop_impl(&env),
104             Err(_) => {
105                 warn!("Dropping a GlobalRef in a detached thread. Fix your code if this message appears frequently (see the GlobalRef docs).");
106                 self.vm
107                     .attach_current_thread()
108                     .and_then(|env| drop_impl(&env))
109             }
110         };
111 
112         if let Err(err) = res {
113             debug!("error dropping global ref: {:#?}", err);
114         }
115     }
116 }
117