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