jni/wrapper/objects/global_ref.rs
1use std::{mem, ops::Deref, sync::Arc};
2
3use log::{debug, warn};
4
5use 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)]
29pub struct GlobalRef {
30 inner: Arc<GlobalRefGuard>,
31}
32
33#[derive(Debug)]
34struct GlobalRefGuard {
35 obj: JObject<'static>,
36 vm: JavaVM,
37}
38
39impl AsRef<GlobalRef> for GlobalRef {
40 fn as_ref(&self) -> &GlobalRef {
41 self
42 }
43}
44
45impl AsRef<JObject<'static>> for GlobalRef {
46 fn as_ref(&self) -> &JObject<'static> {
47 self
48 }
49}
50
51impl Deref for GlobalRef {
52 type Target = JObject<'static>;
53
54 fn deref(&self) -> &Self::Target {
55 &self.inner.obj
56 }
57}
58
59impl 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.
65 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.
75 pub fn as_obj(&self) -> &JObject<'static> {
76 self.as_ref()
77 }
78}
79
80impl GlobalRefGuard {
81 /// Creates a new global reference guard. This assumes that `NewGlobalRef`
82 /// has already been called.
83 unsafe fn from_raw(vm: JavaVM, obj: sys::jobject) -> Self {
84 GlobalRefGuard {
85 obj: JObject::from_raw(obj),
86 vm,
87 }
88 }
89}
90
91impl Drop for GlobalRefGuard {
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}