#![cfg(feature = "invocation")] use std::{sync::Arc, thread::spawn}; use jni::{ errors::{Error, JniError}, Executor, JavaVM, }; mod util; use util::jvm; /// Checks if nested attaches are working properly and threads detach themselves /// on exit. #[test] fn nested_attach() { let executor = Executor::new(jvm().clone()); assert_eq!(jvm().threads_attached(), 0); let thread = spawn(|| { assert_eq!(jvm().threads_attached(), 0); check_nested_attach(jvm(), executor); assert_eq!(jvm().threads_attached(), 1); }); thread.join().unwrap(); assert_eq!(jvm().threads_attached(), 0); } /// Checks if nested `with_attached` calls does not detach the thread before the /// outer-most call is finished. fn check_nested_attach(vm: &Arc, executor: Executor) { check_detached(vm); executor .with_attached(|_| { check_attached(vm); executor.with_attached(|_| { check_attached(vm); Ok(()) })?; check_attached(vm); Ok(()) }) .unwrap(); } fn check_attached(vm: &JavaVM) { assert!(is_attached(vm)); } fn check_detached(vm: &JavaVM) { assert!(!is_attached(vm)); } fn is_attached(vm: &JavaVM) -> bool { vm.get_env() .map(|_| true) .or_else(|jni_err| match jni_err { Error::JniCall(JniError::ThreadDetached) => Ok(false), _ => Err(jni_err), }) .expect("An unexpected JNI error occurred") }