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