1 use std::{ 2 cell::RefCell, 3 ops::{Deref, DerefMut}, 4 ptr, 5 sync::atomic::{AtomicUsize, Ordering}, 6 thread::{current, Thread}, 7 }; 8 9 use log::{debug, error}; 10 11 use crate::{errors::*, sys, JNIEnv}; 12 13 #[cfg(feature = "invocation")] 14 use { 15 crate::InitArgs, 16 std::os::raw::c_void, 17 std::{ffi::OsStr, path::PathBuf}, 18 }; 19 20 /// The Java VM, providing [Invocation API][invocation-api] support. 21 /// 22 /// The JavaVM can be obtained either via [`JNIEnv#get_java_vm`][get-vm] in an already attached 23 /// thread, or it can be [launched](#launching-jvm-from-rust) from Rust via `JavaVM#new`. 24 /// 25 /// ## Attaching Native Threads 26 /// 27 /// A native thread must «attach» itself to be able to call Java methods outside of a native Java 28 /// method. This library provides two modes of attachment, each ensuring the thread is promptly 29 /// detached: 30 /// * A scoped attachment with [`attach_current_thread`][act]. 31 /// The thread will automatically detach itself once the returned guard is dropped. 32 /// * A permanent attachment with [`attach_current_thread_permanently`][actp] 33 /// or [`attach_current_thread_as_daemon`][actd]. 34 /// The thread will automatically detach itself before it terminates. 35 /// 36 /// As attachment and detachment of a thread is an expensive operation, the scoped attachment 37 /// shall be used if happens infrequently. If you have an undefined scope where you need 38 /// to use `JNIEnv` and cannot keep the `AttachGuard`, consider attaching the thread 39 /// permanently. 40 /// 41 /// ### Local Reference Management 42 /// 43 /// Remember that the native thread attached to the VM **must** manage the local references 44 /// properly, i.e., do not allocate an excessive number of references and release them promptly 45 /// when they are no longer needed to enable the GC to collect them. A common approach is to use 46 /// an appropriately-sized local frame for larger code fragments 47 /// (see [`with_local_frame`](struct.JNIEnv.html#method.with_local_frame) and [Executor](#executor)) 48 /// and [auto locals](struct.JNIEnv.html#method.auto_local) in loops. 49 /// 50 /// See also the [JNI specification][spec-references] for details on referencing Java objects. 51 /// 52 /// ### Executor 53 /// 54 /// Jni-rs provides an [`Executor`](struct.Executor.html) — a helper struct that allows to 55 /// execute a closure with `JNIEnv`. It combines the performance benefits of permanent attaches 56 /// *and* automatic local reference management. Prefer it to manual permanent attaches if 57 /// they happen in various parts of the code to reduce the burden of local reference management. 58 /// 59 /// ## Launching JVM from Rust 60 /// 61 /// To [launch][launch-vm] a JVM from a native process, enable the `invocation` 62 /// feature in the Cargo.toml: 63 /// 64 /// ```toml 65 /// jni = { version = "0.21.1", features = ["invocation"] } 66 /// ``` 67 /// 68 /// The application will be able to use [`JavaVM::new`] which will dynamically 69 /// load a `jvm` library (which is distributed with the JVM) at runtime: 70 /// 71 /// ```rust 72 /// # use jni::errors; 73 /// # // 74 /// # // Ignore this test without invocation feature, so that simple `cargo test` works 75 /// # #[cfg(feature = "invocation")] 76 /// # fn main() -> errors::StartJvmResult<()> { 77 /// # use jni::{AttachGuard, objects::JValue, InitArgsBuilder, JNIEnv, JNIVersion, JavaVM, sys::jint}; 78 /// # // 79 /// // Build the VM properties 80 /// let jvm_args = InitArgsBuilder::new() 81 /// // Pass the JNI API version (default is 8) 82 /// .version(JNIVersion::V8) 83 /// // You can additionally pass any JVM options (standard, like a system property, 84 /// // or VM-specific). 85 /// // Here we enable some extra JNI checks useful during development 86 /// .option("-Xcheck:jni") 87 /// .build() 88 /// .unwrap(); 89 /// 90 /// // Create a new VM 91 /// let jvm = JavaVM::new(jvm_args)?; 92 /// 93 /// // Attach the current thread to call into Java — see extra options in 94 /// // "Attaching Native Threads" section. 95 /// // 96 /// // This method returns the guard that will detach the current thread when dropped, 97 /// // also freeing any local references created in it 98 /// let mut env = jvm.attach_current_thread()?; 99 /// 100 /// // Call Java Math#abs(-10) 101 /// let x = JValue::from(-10); 102 /// let val: jint = env.call_static_method("java/lang/Math", "abs", "(I)I", &[x])? 103 /// .i()?; 104 /// 105 /// assert_eq!(val, 10); 106 /// 107 /// # Ok(()) } 108 /// # 109 /// # // This is a stub that gets run instead if the invocation feature is not built 110 /// # #[cfg(not(feature = "invocation"))] 111 /// # fn main() {} 112 /// ``` 113 /// 114 /// At runtime, the JVM installation path is determined via the [java-locator] crate: 115 /// 1. By the `JAVA_HOME` environment variable, if it is set. 116 /// 2. Otherwise — from `java` output. 117 /// 118 /// It is recommended to set `JAVA_HOME` 119 /// 120 /// For the operating system to correctly load the `jvm` library it may also be 121 /// necessary to update the path that the OS uses to find dependencies of the 122 /// `jvm` library. 123 /// * On **Windows**, append the path to `$JAVA_HOME/bin` to the `PATH` environment variable. 124 /// * On **MacOS**, append the path to `libjvm.dylib` to `LD_LIBRARY_PATH` environment variable. 125 /// * On **Linux**, append the path to `libjvm.so` to `LD_LIBRARY_PATH` environment variable. 126 /// 127 /// The exact relative path to `jvm` library is version-specific. 128 /// 129 /// [invocation-api]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/invocation.html 130 /// [get-vm]: struct.JNIEnv.html#method.get_java_vm 131 /// [launch-vm]: struct.JavaVM.html#method.new 132 /// [act]: struct.JavaVM.html#method.attach_current_thread 133 /// [actp]: struct.JavaVM.html#method.attach_current_thread_permanently 134 /// [actd]: struct.JavaVM.html#method.attach_current_thread_as_daemon 135 /// [spec-references]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/design.html#referencing-java-objects 136 /// [java-locator]: https://crates.io/crates/java-locator 137 #[repr(transparent)] 138 #[derive(Debug)] 139 pub struct JavaVM(*mut sys::JavaVM); 140 141 unsafe impl Send for JavaVM {} 142 unsafe impl Sync for JavaVM {} 143 144 impl JavaVM { 145 /// Launch a new JavaVM using the provided init args. 146 /// 147 /// Unlike original JNI API, the main thread (the thread from which this method is called) will 148 /// not be attached to JVM. You must explicitly use `attach_current_thread…` methods (refer 149 /// to [Attaching Native Threads section](#attaching-native-threads)). 150 /// 151 /// *This API requires the "invocation" feature to be enabled, 152 /// see ["Launching JVM from Rust"](struct.JavaVM.html#launching-jvm-from-rust).* 153 /// 154 /// This will attempt to locate a JVM using 155 /// [java-locator], if the JVM has not already been loaded. Use the 156 /// [`with_libjvm`][Self::with_libjvm] method to give an explicit location for the JVM shared 157 /// library (`jvm.dll`, `libjvm.so`, or `libjvm.dylib`, depending on the platform). 158 #[cfg(feature = "invocation")] new(args: InitArgs) -> StartJvmResult<Self>159 pub fn new(args: InitArgs) -> StartJvmResult<Self> { 160 Self::with_libjvm(args, || { 161 Ok([ 162 java_locator::locate_jvm_dyn_library() 163 .map_err(StartJvmError::NotFound)? 164 .as_str(), 165 java_locator::get_jvm_dyn_lib_file_name(), 166 ] 167 .iter() 168 .collect::<PathBuf>()) 169 }) 170 } 171 172 /// Launch a new JavaVM using the provided init args, loading it from the given shared library file if it's not already loaded. 173 /// 174 /// Unlike original JNI API, the main thread (the thread from which this method is called) will 175 /// not be attached to JVM. You must explicitly use `attach_current_thread…` methods (refer 176 /// to [Attaching Native Threads section](#attaching-native-threads)). 177 /// 178 /// *This API requires the "invocation" feature to be enabled, 179 /// see ["Launching JVM from Rust"](struct.JavaVM.html#launching-jvm-from-rust).* 180 /// 181 /// The `libjvm_path` parameter takes a *closure* which returns the path to the JVM shared 182 /// library. The closure is only called if the JVM is not already loaded. Any work that needs 183 /// to be done to locate the JVM shared library should be done inside that closure. 184 #[cfg(feature = "invocation")] with_libjvm<P: AsRef<OsStr>>( args: InitArgs, libjvm_path: impl FnOnce() -> StartJvmResult<P>, ) -> StartJvmResult<Self>185 pub fn with_libjvm<P: AsRef<OsStr>>( 186 args: InitArgs, 187 libjvm_path: impl FnOnce() -> StartJvmResult<P>, 188 ) -> StartJvmResult<Self> { 189 // Determine the path to the shared library. 190 let libjvm_path = libjvm_path()?; 191 let libjvm_path_string = libjvm_path.as_ref().to_string_lossy().into_owned(); 192 193 // Try to load it. 194 let libjvm = match unsafe { libloading::Library::new(libjvm_path.as_ref()) } { 195 Ok(ok) => ok, 196 Err(error) => return Err(StartJvmError::LoadError(libjvm_path_string, error)), 197 }; 198 199 unsafe { 200 // Try to find the `JNI_CreateJavaVM` function in the loaded library. 201 let create_fn = libjvm 202 .get(b"JNI_CreateJavaVM\0") 203 .map_err(|error| StartJvmError::LoadError(libjvm_path_string.to_owned(), error))?; 204 205 // Create the JVM. 206 Self::with_create_fn_ptr(args, *create_fn).map_err(StartJvmError::Create) 207 } 208 } 209 210 #[cfg(feature = "invocation")] with_create_fn_ptr( args: InitArgs, create_fn_ptr: unsafe extern "system" fn( pvm: *mut *mut sys::JavaVM, penv: *mut *mut c_void, args: *mut c_void, ) -> sys::jint, ) -> Result<Self>211 unsafe fn with_create_fn_ptr( 212 args: InitArgs, 213 create_fn_ptr: unsafe extern "system" fn( 214 pvm: *mut *mut sys::JavaVM, 215 penv: *mut *mut c_void, 216 args: *mut c_void, 217 ) -> sys::jint, 218 ) -> Result<Self> { 219 let mut ptr: *mut sys::JavaVM = ::std::ptr::null_mut(); 220 let mut env: *mut sys::JNIEnv = ::std::ptr::null_mut(); 221 222 jni_error_code_to_result(create_fn_ptr( 223 &mut ptr as *mut _, 224 &mut env as *mut *mut sys::JNIEnv as *mut *mut c_void, 225 args.inner_ptr(), 226 ))?; 227 228 let vm = Self::from_raw(ptr)?; 229 java_vm_unchecked!(vm.0, DetachCurrentThread); 230 231 Ok(vm) 232 } 233 234 /// Create a JavaVM from a raw pointer. 235 /// 236 /// # Safety 237 /// 238 /// Expects a valid pointer retrieved from the `JNI_CreateJavaVM` JNI function. Only does null check. from_raw(ptr: *mut sys::JavaVM) -> Result<Self>239 pub unsafe fn from_raw(ptr: *mut sys::JavaVM) -> Result<Self> { 240 non_null!(ptr, "from_raw ptr argument"); 241 Ok(JavaVM(ptr)) 242 } 243 244 /// Returns underlying `sys::JavaVM` interface. get_java_vm_pointer(&self) -> *mut sys::JavaVM245 pub fn get_java_vm_pointer(&self) -> *mut sys::JavaVM { 246 self.0 247 } 248 249 /// Attaches the current thread to the JVM. Calling this for a thread that is already attached 250 /// is a no-op. 251 /// 252 /// The thread will detach itself automatically when it exits. 253 /// 254 /// Attached threads [block JVM exit][block]. If it is not desirable — consider using 255 /// [`attach_current_thread_as_daemon`][attach-as-daemon]. 256 /// 257 /// [block]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/invocation.html#unloading-the-vm 258 /// [attach-as-daemon]: struct.JavaVM.html#method.attach_current_thread_as_daemon attach_current_thread_permanently(&self) -> Result<JNIEnv>259 pub fn attach_current_thread_permanently(&self) -> Result<JNIEnv> { 260 match self.get_env() { 261 Ok(env) => Ok(env), 262 Err(_) => self.attach_current_thread_impl(ThreadType::Normal), 263 } 264 } 265 266 /// Attaches the current thread to the Java VM. The returned `AttachGuard` 267 /// can be dereferenced to a `JNIEnv` and automatically detaches the thread 268 /// when dropped. Calling this in a thread that is already attached is a no-op, and 269 /// will neither change its daemon status nor prematurely detach it. 270 /// 271 /// Attached threads [block JVM exit][block]. 272 /// 273 /// Attaching and detaching a thread is an expensive operation. If you use it frequently 274 /// in the same threads, consider either [attaching them permanently][attach-as-daemon], 275 /// or, if the scope where you need the `JNIEnv` is well-defined, keeping the returned guard. 276 /// 277 /// [block]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/invocation.html#unloading-the-vm 278 /// [attach-as-daemon]: struct.JavaVM.html#method.attach_current_thread_as_daemon attach_current_thread(&self) -> Result<AttachGuard>279 pub fn attach_current_thread(&self) -> Result<AttachGuard> { 280 match self.get_env() { 281 Ok(env) => Ok(AttachGuard::new_nested(env)), 282 Err(_) => { 283 let env = self.attach_current_thread_impl(ThreadType::Normal)?; 284 Ok(AttachGuard::new(env)) 285 } 286 } 287 } 288 289 /// Explicitly detaches the current thread from the JVM. 290 /// 291 /// _**Note**: This operation is _rarely_ appropriate to use, because the 292 /// attachment methods [ensure](#attaching-native-threads) that the thread 293 /// is automatically detached._ 294 /// 295 /// Detaching a non-attached thread is a no-op. 296 /// 297 /// To support the use of `JavaVM::destroy()` it may be necessary to use this API to 298 /// explicitly detach daemon threads before `JavaVM::destroy()` is called because 299 /// `JavaVM::destroy()` does not synchronize and wait for daemon threads. 300 /// 301 /// Any daemon thread that is still "attached" after `JavaVM::destroy()` returns would 302 /// cause undefined behaviour if it then tries to make any JNI calls or tries 303 /// to detach itself. 304 /// 305 /// Normally `jni-rs` will automatically detach threads from the `JavaVM` by storing 306 /// a guard in thread-local-storage that will detach on `Drop` but this will cause 307 /// undefined behaviour if `JavaVM::destroy()` has been called. 308 /// 309 /// Calling this will clear the thread-local-storage guard and detach the thread 310 /// early to avoid any attempt to automatically detach when the thread exits. 311 /// 312 /// # Safety 313 /// 314 /// __Any existing `JNIEnv`s and `AttachGuard`s created in the calling thread 315 /// will be invalidated after this method completes. It is the__ caller’s __responsibility 316 /// to ensure that no JNI calls are subsequently performed on these objects.__ 317 /// Failure to do so will result in unspecified errors, possibly, the process crash. 318 /// 319 /// Given some care is exercised, this method can be used to detach permanently attached 320 /// threads _before_ they exit (when automatic detachment occurs). However, it is 321 /// never appropriate to use it with the scoped attachment (`attach_current_thread`). 322 // This method is hidden because it is almost never needed and its use requires some 323 // extra care. Its status might be reconsidered if we learn of any use cases that require it. detach_current_thread(&self)324 pub unsafe fn detach_current_thread(&self) { 325 InternalAttachGuard::clear_tls(); 326 } 327 328 /// Attaches the current thread to the Java VM as a _daemon_. Calling this in a thread 329 /// that is already attached is a no-op, and will not change its status to a daemon thread. 330 /// 331 /// The thread will detach itself automatically when it exits. attach_current_thread_as_daemon(&self) -> Result<JNIEnv>332 pub fn attach_current_thread_as_daemon(&self) -> Result<JNIEnv> { 333 match self.get_env() { 334 Ok(env) => Ok(env), 335 Err(_) => self.attach_current_thread_impl(ThreadType::Daemon), 336 } 337 } 338 339 /// Returns the current number of threads attached to the JVM. 340 /// 341 /// This method is provided mostly for diagnostic purposes. threads_attached(&self) -> usize342 pub fn threads_attached(&self) -> usize { 343 ATTACHED_THREADS.load(Ordering::SeqCst) 344 } 345 346 /// Get the `JNIEnv` associated with the current thread, or 347 /// `ErrorKind::Detached` 348 /// if the current thread is not attached to the java VM. get_env(&self) -> Result<JNIEnv>349 pub fn get_env(&self) -> Result<JNIEnv> { 350 let mut ptr = ptr::null_mut(); 351 unsafe { 352 let res = java_vm_unchecked!(self.0, GetEnv, &mut ptr, sys::JNI_VERSION_1_1); 353 jni_error_code_to_result(res)?; 354 355 JNIEnv::from_raw(ptr as *mut sys::JNIEnv) 356 } 357 } 358 359 /// Creates `InternalAttachGuard` and attaches current thread. attach_current_thread_impl(&self, thread_type: ThreadType) -> Result<JNIEnv>360 fn attach_current_thread_impl(&self, thread_type: ThreadType) -> Result<JNIEnv> { 361 let guard = InternalAttachGuard::new(self.get_java_vm_pointer()); 362 let env_ptr = unsafe { 363 if thread_type == ThreadType::Daemon { 364 guard.attach_current_thread_as_daemon()? 365 } else { 366 guard.attach_current_thread()? 367 } 368 }; 369 370 InternalAttachGuard::fill_tls(guard); 371 372 unsafe { JNIEnv::from_raw(env_ptr as *mut sys::JNIEnv) } 373 } 374 375 /// Unloads the JavaVM and frees all it's associated resources 376 /// 377 /// Firstly if this thread is not already attached to the `JavaVM` then 378 /// it will be attached. 379 /// 380 /// This thread will then wait until there are no other non-daemon threads 381 /// attached to the `JavaVM` before unloading it (including threads spawned 382 /// by Java and those that are attached via JNI) 383 /// 384 /// # Safety 385 /// 386 /// IF YOU ARE USING DAEMON THREADS THIS MAY BE DIFFICULT TO USE SAFELY! 387 /// 388 /// ## Daemon thread rules 389 /// 390 /// Since the JNI spec makes it clear that `DestroyJavaVM` will not wait for 391 /// attached deamon threads to exit, this also means that if you do have any 392 /// attached daemon threads it is your responsibility to ensure that they 393 /// don't try and use JNI after the `JavaVM` is destroyed and you won't be able 394 /// to detach them after the `JavaVM` has been destroyed. 395 /// 396 /// This creates a very unsafe hazard in `jni-rs` because it normally automatically 397 /// ensures that any thread that gets attached will be detached before it exits. 398 /// 399 /// Normally `jni-rs` will automatically detach threads from the `JavaVM` by storing 400 /// a guard in thread-local-storage that will detach on `Drop` but this will cause 401 /// undefined behaviour if `JavaVM::destroy()` has been called before the thread 402 /// exits. 403 /// 404 /// To clear this thread-local-storage guard from daemon threads you can call 405 /// [`JavaVM::detach_current_thread()`] within each daemon thread, before calling 406 /// this API. 407 /// 408 /// Calling this will clear the thread-local-storage guard and detach the thread 409 /// early to avoid any attempt to automatically detach when the thread exits. 410 /// 411 /// ## Don't call from a Java native function 412 /// 413 /// There must be no Java methods on the call stack when `JavaVM::destroy()` is called. 414 /// 415 /// ## Drop all JNI state, including auto-release types before calling `JavaVM::destroy()` 416 /// 417 /// There is currently no `'vm` lifetime associated with a `JavaVM` that 418 /// would allow the borrow checker to enforce that all `jni` resources 419 /// associated with the `JavaVM` have been released. 420 /// 421 /// Since these JNI resources could lead to undefined behaviour through any 422 /// use after the `JavaVM` has been destroyed then it is your responsibility 423 /// to release these resources. 424 /// 425 /// In particular, there are numerous auto-release types in the `jni` API 426 /// that will automatically make JNI calls within their `Drop` 427 /// implementation. All such types _must_ be dropped before `destroy()` is 428 /// called to avoid undefined bahaviour. 429 /// 430 /// Here is an non-exhaustive list of auto-release types to consider: 431 /// - `AttachGuard` 432 /// - `AutoElements` 433 /// - `AutoElementsCritical` 434 /// - `AutoLocal` 435 /// - `GlobalRef` 436 /// - `JavaStr` 437 /// - `JMap` 438 /// - `WeakRef` 439 /// 440 /// ## Invalid `JavaVM` on return 441 /// 442 /// After `destroy()` returns then the `JavaVM` will be in an undefined state 443 /// and must be dropped (e.g. via `std::mem::drop()`) to avoid undefined behaviour. 444 /// 445 /// This method doesn't take ownership of the `JavaVM` before it is 446 /// destroyed because the `JavaVM` may have been shared (E.g. via an `Arc`) 447 /// between all the threads that have not yet necessarily exited before this 448 /// is called. 449 /// 450 /// So although the `JavaVM` won't necessarily be solely owned by this 451 /// thread when `destroy()` is first called it will conceptually own the 452 /// `JavaVM` before `destroy()` returns. destroy(&self) -> Result<()>453 pub unsafe fn destroy(&self) -> Result<()> { 454 unsafe { 455 let res = java_vm_unchecked!(self.0, DestroyJavaVM); 456 jni_error_code_to_result(res) 457 } 458 } 459 } 460 461 thread_local! { 462 static THREAD_ATTACH_GUARD: RefCell<Option<InternalAttachGuard>> = RefCell::new(None) 463 } 464 465 static ATTACHED_THREADS: AtomicUsize = AtomicUsize::new(0); 466 467 /// A RAII implementation of scoped guard which detaches the current thread 468 /// when dropped. The attached `JNIEnv` can be accessed through this guard 469 /// via its `Deref` implementation. 470 pub struct AttachGuard<'local> { 471 env: JNIEnv<'local>, 472 should_detach: bool, 473 } 474 475 impl<'local> AttachGuard<'local> { 476 /// AttachGuard created with this method will detach current thread on drop new(env: JNIEnv<'local>) -> Self477 fn new(env: JNIEnv<'local>) -> Self { 478 Self { 479 env, 480 should_detach: true, 481 } 482 } 483 484 /// AttachGuard created with this method will not detach current thread on drop, which is 485 /// the case for nested attaches. new_nested(env: JNIEnv<'local>) -> Self486 fn new_nested(env: JNIEnv<'local>) -> Self { 487 Self { 488 env, 489 should_detach: false, 490 } 491 } 492 } 493 494 impl<'local> Deref for AttachGuard<'local> { 495 type Target = JNIEnv<'local>; 496 deref(&self) -> &Self::Target497 fn deref(&self) -> &Self::Target { 498 &self.env 499 } 500 } 501 502 impl<'local> DerefMut for AttachGuard<'local> { deref_mut(&mut self) -> &mut Self::Target503 fn deref_mut(&mut self) -> &mut Self::Target { 504 &mut self.env 505 } 506 } 507 508 impl<'local> Drop for AttachGuard<'local> { drop(&mut self)509 fn drop(&mut self) { 510 if self.should_detach { 511 InternalAttachGuard::clear_tls(); 512 } 513 } 514 } 515 516 #[derive(PartialEq)] 517 enum ThreadType { 518 Normal, 519 Daemon, 520 } 521 522 #[derive(Debug)] 523 struct InternalAttachGuard { 524 java_vm: *mut sys::JavaVM, 525 /// A call std::thread::current() function can panic in case the local data has been destroyed 526 /// before the thead local variables. The possibility of this happening depends on the platform 527 /// implementation of the crate::sys_common::thread_local_dtor::register_dtor_fallback. 528 /// The InternalAttachGuard is a thread-local vairable, so capture the thread meta-data 529 /// during creation 530 thread: Thread, 531 } 532 533 impl InternalAttachGuard { new(java_vm: *mut sys::JavaVM) -> Self534 fn new(java_vm: *mut sys::JavaVM) -> Self { 535 Self { 536 java_vm, 537 thread: current(), 538 } 539 } 540 541 /// Stores guard in thread local storage. fill_tls(guard: InternalAttachGuard)542 fn fill_tls(guard: InternalAttachGuard) { 543 THREAD_ATTACH_GUARD.with(move |f| { 544 *f.borrow_mut() = Some(guard); 545 }); 546 } 547 548 /// Clears thread local storage, dropping the InternalAttachGuard and causing detach of 549 /// the current thread. clear_tls()550 fn clear_tls() { 551 THREAD_ATTACH_GUARD.with(move |f| { 552 *f.borrow_mut() = None; 553 }); 554 } 555 attach_current_thread(&self) -> Result<*mut sys::JNIEnv>556 unsafe fn attach_current_thread(&self) -> Result<*mut sys::JNIEnv> { 557 let mut env_ptr = ptr::null_mut(); 558 let res = java_vm_unchecked!( 559 self.java_vm, 560 AttachCurrentThread, 561 &mut env_ptr, 562 ptr::null_mut() 563 ); 564 jni_error_code_to_result(res)?; 565 566 ATTACHED_THREADS.fetch_add(1, Ordering::SeqCst); 567 568 debug!( 569 "Attached thread {} ({:?}). {} threads attached", 570 self.thread.name().unwrap_or_default(), 571 self.thread.id(), 572 ATTACHED_THREADS.load(Ordering::SeqCst) 573 ); 574 575 Ok(env_ptr as *mut sys::JNIEnv) 576 } 577 attach_current_thread_as_daemon(&self) -> Result<*mut sys::JNIEnv>578 unsafe fn attach_current_thread_as_daemon(&self) -> Result<*mut sys::JNIEnv> { 579 let mut env_ptr = ptr::null_mut(); 580 let res = java_vm_unchecked!( 581 self.java_vm, 582 AttachCurrentThreadAsDaemon, 583 &mut env_ptr, 584 ptr::null_mut() 585 ); 586 jni_error_code_to_result(res)?; 587 588 ATTACHED_THREADS.fetch_add(1, Ordering::SeqCst); 589 590 debug!( 591 "Attached daemon thread {} ({:?}). {} threads attached", 592 self.thread.name().unwrap_or_default(), 593 self.thread.id(), 594 ATTACHED_THREADS.load(Ordering::SeqCst) 595 ); 596 597 Ok(env_ptr as *mut sys::JNIEnv) 598 } 599 detach(&mut self) -> Result<()>600 fn detach(&mut self) -> Result<()> { 601 unsafe { 602 java_vm_unchecked!(self.java_vm, DetachCurrentThread); 603 } 604 ATTACHED_THREADS.fetch_sub(1, Ordering::SeqCst); 605 debug!( 606 "Detached thread {} ({:?}). {} threads remain attached", 607 self.thread.name().unwrap_or_default(), 608 self.thread.id(), 609 ATTACHED_THREADS.load(Ordering::SeqCst) 610 ); 611 612 Ok(()) 613 } 614 } 615 616 impl Drop for InternalAttachGuard { drop(&mut self)617 fn drop(&mut self) { 618 if let Err(e) = self.detach() { 619 error!( 620 "Error detaching current thread: {:#?}\nThread {} id={:?}", 621 e, 622 self.thread.name().unwrap_or_default(), 623 self.thread.id(), 624 ); 625 } 626 } 627 } 628