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