1 use std::{ 2 cell::RefCell, 3 ops::Deref, 4 ptr, 5 sync::atomic::{AtomicUsize, Ordering}, 6 thread::current, 7 }; 8 9 use log::{debug, error}; 10 11 use crate::{errors::*, sys, JNIEnv}; 12 13 #[cfg(feature = "invocation")] 14 use crate::InitArgs; 15 16 /// The Java VM, providing [Invocation API][invocation-api] support. 17 /// 18 /// The JavaVM can be obtained either via [`JNIEnv#get_java_vm`][get-vm] in an already attached 19 /// thread, or it can be [launched](#launching-jvm-from-rust) from Rust via `JavaVM#new`. 20 /// 21 /// ## Attaching Native Threads 22 /// 23 /// A native thread must «attach» itself to be able to call Java methods outside of a native Java 24 /// method. This library provides two modes of attachment, each ensuring the thread is promptly 25 /// detached: 26 /// * A scoped attachment with [`attach_current_thread`][act]. 27 /// The thread will automatically detach itself once the returned guard is dropped. 28 /// * A permanent attachment with [`attach_current_thread_permanently`][actp] 29 /// or [`attach_current_thread_as_daemon`][actd]. 30 /// The thread will automatically detach itself before it terminates. 31 /// 32 /// As attachment and detachment of a thread is an expensive operation, the scoped attachment 33 /// shall be used if happens infrequently. If you have an undefined scope where you need 34 /// to use `JNIEnv` and cannot keep the `AttachGuard`, consider attaching the thread 35 /// permanently. 36 /// 37 /// ### Local Reference Management 38 /// 39 /// Remember that the native thread attached to the VM **must** manage the local references 40 /// properly, i.e., do not allocate an excessive number of references and release them promptly 41 /// when they are no longer needed to enable the GC to collect them. A common approach is to use 42 /// an appropriately-sized local frame for larger code fragments 43 /// (see [`with_local_frame`](struct.JNIEnv.html#method.with_local_frame) and [Executor](#executor)) 44 /// and [auto locals](struct.JNIEnv.html#method.auto_local) in loops. 45 /// 46 /// See also the [JNI specification][spec-references] for details on referencing Java objects. 47 /// 48 /// ### Executor 49 /// 50 /// Jni-rs provides an [`Executor`](struct.Executor.html) — a helper struct that allows to 51 /// execute a closure with `JNIEnv`. It combines the performance benefits of permanent attaches 52 /// *and* automatic local reference management. Prefer it to manual permanent attaches if 53 /// they happen in various parts of the code to reduce the burden of local reference management. 54 /// 55 /// ## Launching JVM from Rust 56 /// 57 /// To [launch][launch-vm] a JVM from a native process, enable the `invocation` feature 58 /// in the Cargo.toml: 59 /// 60 /// ```toml 61 /// jni = { version = "0.19.0", features = ["invocation"] } 62 /// ``` 63 /// 64 /// The application will require linking to the dynamic `jvm` library, which is distributed 65 /// with the JVM, and allow to use `JavaVM#new`: 66 /// 67 /// ```rust 68 /// # use jni::errors; 69 /// # // 70 /// # fn main() -> errors::Result<()> { 71 /// # // Ignore this test without invocation feature, so that simple `cargo test` works 72 /// # #[cfg(feature = "invocation")] { 73 /// # // 74 /// # use jni::{AttachGuard, objects::JValue, InitArgsBuilder, JNIEnv, JNIVersion, JavaVM, sys::jint}; 75 /// # // 76 /// // Build the VM properties 77 /// let jvm_args = InitArgsBuilder::new() 78 /// // Pass the JNI API version (default is 8) 79 /// .version(JNIVersion::V8) 80 /// // You can additionally pass any JVM options (standard, like a system property, 81 /// // or VM-specific). 82 /// // Here we enable some extra JNI checks useful during development 83 /// .option("-Xcheck:jni") 84 /// .build() 85 /// .unwrap(); 86 /// 87 /// // Create a new VM 88 /// let jvm = JavaVM::new(jvm_args)?; 89 /// 90 /// // Attach the current thread to call into Java — see extra options in 91 /// // "Attaching Native Threads" section. 92 /// // 93 /// // This method returns the guard that will detach the current thread when dropped, 94 /// // also freeing any local references created in it 95 /// let env = jvm.attach_current_thread()?; 96 /// 97 /// // Call Java Math#abs(-10) 98 /// let x = JValue::from(-10); 99 /// let val: jint = env.call_static_method("java/lang/Math", "abs", "(I)I", &[x])? 100 /// .i()?; 101 /// 102 /// assert_eq!(val, 10); 103 /// 104 /// # } 105 /// # Ok(()) } 106 /// ``` 107 /// 108 /// During build time, the JVM installation path is determined: 109 /// 1. By `JAVA_HOME` environment variable, if it is set. 110 /// 2. Otherwise — from `java` output. 111 /// 112 /// It is recommended to set `JAVA_HOME` to have reproducible builds, 113 /// especially, in case of multiple VMs installed. 114 /// 115 /// At application run time, you must specify the path 116 /// to the `jvm` library so that the loader can locate it. 117 /// * On **Windows**, append the path to `jvm.dll` to `PATH` environment variable. 118 /// * On **MacOS**, append the path to `libjvm.dylib` to `LD_LIBRARY_PATH` environment variable. 119 /// * On **Linux**, append the path to `libjvm.so` to `LD_LIBRARY_PATH` environment variable. 120 /// 121 /// The exact relative path to `jvm` library is version-specific. 122 /// 123 /// For more information on linking — see documentation 124 /// in [build.rs](https://github.com/jni-rs/jni-rs/tree/master/build.rs). 125 /// 126 /// [invocation-api]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/invocation.html 127 /// [get-vm]: struct.JNIEnv.html#method.get_java_vm 128 /// [launch-vm]: struct.JavaVM.html#method.new 129 /// [act]: struct.JavaVM.html#method.attach_current_thread 130 /// [actp]: struct.JavaVM.html#method.attach_current_thread_permanently 131 /// [actd]: struct.JavaVM.html#method.attach_current_thread_as_daemon 132 /// [spec-references]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/design.html#referencing-java-objects 133 #[repr(transparent)] 134 pub struct JavaVM(*mut sys::JavaVM); 135 136 unsafe impl Send for JavaVM {} 137 unsafe impl Sync for JavaVM {} 138 139 impl JavaVM { 140 /// Launch a new JavaVM using the provided init args. 141 /// 142 /// Unlike original JNI API, the main thread (the thread from which this method is called) will 143 /// not be attached to JVM. You must explicitly use `attach_current_thread…` methods (refer 144 /// to [Attaching Native Threads section](#attaching-native-threads)). 145 /// 146 /// *This API requires "invocation" feature to be enabled, 147 /// see ["Launching JVM from Rust"](struct.JavaVM.html#launching-jvm-from-rust).* 148 #[cfg(feature = "invocation")] new(args: InitArgs) -> Result<Self>149 pub fn new(args: InitArgs) -> Result<Self> { 150 use std::os::raw::c_void; 151 152 let mut ptr: *mut sys::JavaVM = ::std::ptr::null_mut(); 153 let mut env: *mut sys::JNIEnv = ::std::ptr::null_mut(); 154 155 unsafe { 156 jni_error_code_to_result(sys::JNI_CreateJavaVM( 157 &mut ptr as *mut _, 158 &mut env as *mut *mut sys::JNIEnv as *mut *mut c_void, 159 args.inner_ptr(), 160 ))?; 161 162 let vm = Self::from_raw(ptr)?; 163 java_vm_unchecked!(vm.0, DetachCurrentThread); 164 165 Ok(vm) 166 } 167 } 168 169 /// Create a JavaVM from a raw pointer. 170 /// 171 /// # Safety 172 /// 173 /// Expects a valid pointer retrieved from the `JNI_CreateJavaVM` JNI function. Only does null check. from_raw(ptr: *mut sys::JavaVM) -> Result<Self>174 pub unsafe fn from_raw(ptr: *mut sys::JavaVM) -> Result<Self> { 175 non_null!(ptr, "from_raw ptr argument"); 176 Ok(JavaVM(ptr)) 177 } 178 179 /// Returns underlying `sys::JavaVM` interface. get_java_vm_pointer(&self) -> *mut sys::JavaVM180 pub fn get_java_vm_pointer(&self) -> *mut sys::JavaVM { 181 self.0 182 } 183 184 /// Attaches the current thread to the JVM. Calling this for a thread that is already attached 185 /// is a no-op. 186 /// 187 /// The thread will detach itself automatically when it exits. 188 /// 189 /// Attached threads [block JVM exit][block]. If it is not desirable — consider using 190 /// [`attach_current_thread_as_daemon`][attach-as-daemon]. 191 /// 192 /// [block]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/invocation.html#unloading-the-vm 193 /// [attach-as-daemon]: struct.JavaVM.html#method.attach_current_thread_as_daemon attach_current_thread_permanently(&self) -> Result<JNIEnv>194 pub fn attach_current_thread_permanently(&self) -> Result<JNIEnv> { 195 match self.get_env() { 196 Ok(env) => Ok(env), 197 Err(_) => self.attach_current_thread_impl(ThreadType::Normal), 198 } 199 } 200 201 /// Attaches the current thread to the Java VM. The returned `AttachGuard` 202 /// can be dereferenced to a `JNIEnv` and automatically detaches the thread 203 /// when dropped. Calling this in a thread that is already attached is a no-op, and 204 /// will neither change its daemon status nor prematurely detach it. 205 /// 206 /// Attached threads [block JVM exit][block]. 207 /// 208 /// Attaching and detaching a thread is an expensive operation. If you use it frequently 209 /// in the same threads, consider either [attaching them permanently][attach-as-daemon], 210 /// or, if the scope where you need the `JNIEnv` is well-defined, keeping the returned guard. 211 /// 212 /// [block]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/invocation.html#unloading-the-vm 213 /// [attach-as-daemon]: struct.JavaVM.html#method.attach_current_thread_as_daemon attach_current_thread(&self) -> Result<AttachGuard>214 pub fn attach_current_thread(&self) -> Result<AttachGuard> { 215 match self.get_env() { 216 Ok(env) => Ok(AttachGuard::new_nested(env)), 217 Err(_) => { 218 let env = self.attach_current_thread_impl(ThreadType::Normal)?; 219 Ok(AttachGuard::new(env)) 220 } 221 } 222 } 223 224 /// Detaches current thread from the JVM. This operation is _rarely_ appropriate to use, 225 /// because the attachment methods [ensure](#attaching-native-threads) that the thread is 226 /// promptly detached. 227 /// 228 /// Detaching a non-attached thread is a no-op. 229 /// 230 /// __Any existing `JNIEnv`s and `AttachGuard`s created in the calling thread 231 /// will be invalidated after this method completes. It is the__ caller’s __responsibility 232 /// to ensure that no JNI calls are subsequently performed on these objects.__ 233 /// Failure to do so will result in unspecified errors, possibly, the process crash. 234 /// 235 /// Given some care is exercised, this method can be used to detach permanently attached 236 /// threads _before_ they exit (when automatic detachment occurs). However, it is 237 /// never appropriate to use it with the scoped attachment (`attach_current_thread`). 238 // This method is hidden because it is almost never needed and its use requires some 239 // extra care. Its status might be reconsidered if we learn of any use cases that require it. 240 #[doc(hidden)] detach_current_thread(&self)241 pub fn detach_current_thread(&self) { 242 InternalAttachGuard::clear_tls(); 243 } 244 245 /// Attaches the current thread to the Java VM as a _daemon_. Calling this in a thread 246 /// that is already attached is a no-op, and will not change its status to a daemon thread. 247 /// 248 /// The thread will detach itself automatically when it exits. attach_current_thread_as_daemon(&self) -> Result<JNIEnv>249 pub fn attach_current_thread_as_daemon(&self) -> Result<JNIEnv> { 250 match self.get_env() { 251 Ok(env) => Ok(env), 252 Err(_) => self.attach_current_thread_impl(ThreadType::Daemon), 253 } 254 } 255 256 /// Returns the current number of threads attached to the JVM. 257 /// 258 /// This method is provided mostly for diagnostic purposes. threads_attached(&self) -> usize259 pub fn threads_attached(&self) -> usize { 260 ATTACHED_THREADS.load(Ordering::SeqCst) 261 } 262 263 /// Get the `JNIEnv` associated with the current thread, or 264 /// `ErrorKind::Detached` 265 /// if the current thread is not attached to the java VM. get_env(&self) -> Result<JNIEnv>266 pub fn get_env(&self) -> Result<JNIEnv> { 267 let mut ptr = ptr::null_mut(); 268 unsafe { 269 let res = java_vm_unchecked!(self.0, GetEnv, &mut ptr, sys::JNI_VERSION_1_1); 270 jni_error_code_to_result(res)?; 271 272 JNIEnv::from_raw(ptr as *mut sys::JNIEnv) 273 } 274 } 275 276 /// Creates `InternalAttachGuard` and attaches current thread. attach_current_thread_impl(&self, thread_type: ThreadType) -> Result<JNIEnv>277 fn attach_current_thread_impl(&self, thread_type: ThreadType) -> Result<JNIEnv> { 278 let guard = InternalAttachGuard::new(self.get_java_vm_pointer()); 279 let env_ptr = unsafe { 280 if thread_type == ThreadType::Daemon { 281 guard.attach_current_thread_as_daemon()? 282 } else { 283 guard.attach_current_thread()? 284 } 285 }; 286 287 InternalAttachGuard::fill_tls(guard); 288 289 unsafe { JNIEnv::from_raw(env_ptr as *mut sys::JNIEnv) } 290 } 291 } 292 293 thread_local! { 294 static THREAD_ATTACH_GUARD: RefCell<Option<InternalAttachGuard>> = RefCell::new(None) 295 } 296 297 static ATTACHED_THREADS: AtomicUsize = AtomicUsize::new(0); 298 299 /// A RAII implementation of scoped guard which detaches the current thread 300 /// when dropped. The attached `JNIEnv` can be accessed through this guard 301 /// via its `Deref` implementation. 302 pub struct AttachGuard<'a> { 303 env: JNIEnv<'a>, 304 should_detach: bool, 305 } 306 307 impl<'a> AttachGuard<'a> { 308 /// AttachGuard created with this method will detach current thread on drop new(env: JNIEnv<'a>) -> Self309 fn new(env: JNIEnv<'a>) -> Self { 310 Self { 311 env, 312 should_detach: true, 313 } 314 } 315 316 /// AttachGuard created with this method will not detach current thread on drop, which is 317 /// the case for nested attaches. new_nested(env: JNIEnv<'a>) -> Self318 fn new_nested(env: JNIEnv<'a>) -> Self { 319 Self { 320 env, 321 should_detach: false, 322 } 323 } 324 } 325 326 impl<'a> Deref for AttachGuard<'a> { 327 type Target = JNIEnv<'a>; 328 deref(&self) -> &Self::Target329 fn deref(&self) -> &Self::Target { 330 &self.env 331 } 332 } 333 334 impl<'a> Drop for AttachGuard<'a> { drop(&mut self)335 fn drop(&mut self) { 336 if self.should_detach { 337 InternalAttachGuard::clear_tls(); 338 } 339 } 340 } 341 342 #[derive(PartialEq)] 343 enum ThreadType { 344 Normal, 345 Daemon, 346 } 347 348 #[derive(Debug)] 349 struct InternalAttachGuard { 350 java_vm: *mut sys::JavaVM, 351 } 352 353 impl InternalAttachGuard { new(java_vm: *mut sys::JavaVM) -> Self354 fn new(java_vm: *mut sys::JavaVM) -> Self { 355 Self { java_vm } 356 } 357 358 /// Stores guard in thread local storage. fill_tls(guard: InternalAttachGuard)359 fn fill_tls(guard: InternalAttachGuard) { 360 THREAD_ATTACH_GUARD.with(move |f| { 361 *f.borrow_mut() = Some(guard); 362 }); 363 } 364 365 /// Clears thread local storage, dropping the InternalAttachGuard and causing detach of 366 /// the current thread. clear_tls()367 fn clear_tls() { 368 THREAD_ATTACH_GUARD.with(move |f| { 369 *f.borrow_mut() = None; 370 }); 371 } 372 attach_current_thread(&self) -> Result<*mut sys::JNIEnv>373 unsafe fn attach_current_thread(&self) -> Result<*mut sys::JNIEnv> { 374 let mut env_ptr = ptr::null_mut(); 375 let res = java_vm_unchecked!( 376 self.java_vm, 377 AttachCurrentThread, 378 &mut env_ptr, 379 ptr::null_mut() 380 ); 381 jni_error_code_to_result(res)?; 382 383 ATTACHED_THREADS.fetch_add(1, Ordering::SeqCst); 384 385 debug!( 386 "Attached thread {} ({:?}). {} threads attached", 387 current().name().unwrap_or_default(), 388 current().id(), 389 ATTACHED_THREADS.load(Ordering::SeqCst) 390 ); 391 392 Ok(env_ptr as *mut sys::JNIEnv) 393 } 394 attach_current_thread_as_daemon(&self) -> Result<*mut sys::JNIEnv>395 unsafe fn attach_current_thread_as_daemon(&self) -> Result<*mut sys::JNIEnv> { 396 let mut env_ptr = ptr::null_mut(); 397 let res = java_vm_unchecked!( 398 self.java_vm, 399 AttachCurrentThreadAsDaemon, 400 &mut env_ptr, 401 ptr::null_mut() 402 ); 403 jni_error_code_to_result(res)?; 404 405 ATTACHED_THREADS.fetch_add(1, Ordering::SeqCst); 406 407 debug!( 408 "Attached daemon thread {} ({:?}). {} threads attached", 409 current().name().unwrap_or_default(), 410 current().id(), 411 ATTACHED_THREADS.load(Ordering::SeqCst) 412 ); 413 414 Ok(env_ptr as *mut sys::JNIEnv) 415 } 416 detach(&mut self) -> Result<()>417 fn detach(&mut self) -> Result<()> { 418 unsafe { 419 java_vm_unchecked!(self.java_vm, DetachCurrentThread); 420 } 421 ATTACHED_THREADS.fetch_sub(1, Ordering::SeqCst); 422 debug!( 423 "Detached thread {} ({:?}). {} threads remain attached", 424 current().name().unwrap_or_default(), 425 current().id(), 426 ATTACHED_THREADS.load(Ordering::SeqCst) 427 ); 428 429 Ok(()) 430 } 431 } 432 433 impl Drop for InternalAttachGuard { drop(&mut self)434 fn drop(&mut self) { 435 if let Err(e) = self.detach() { 436 error!( 437 "Error detaching current thread: {:#?}\nThread {} id={:?}", 438 e, 439 current().name().unwrap_or_default(), 440 current().id(), 441 ); 442 } 443 } 444 } 445