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