use std::sync::Arc; use crate::{errors::*, JNIEnv, JavaVM}; /// The capacity of local frames, allocated for attached threads by default. Same as the default /// value Hotspot uses when calling native Java methods. pub const DEFAULT_LOCAL_FRAME_CAPACITY: i32 = 32; /// Thread attachment manager. It allows to execute closures in attached threads with automatic /// local references management done with `with_local_frame`. It combines the performance benefits /// of permanent attaches whilst removing the risk of local references leaks if used consistently. /// /// Although all locals are freed on closure exit, it might be needed to manually free /// locals _inside_ the closure if an unbounded number of them is created (e.g., in a loop). /// See ["Local Reference Management"](struct.JavaVM.html#local-reference-management) for details. /// /// Threads using the Executor are attached on the first invocation as daemons, /// hence they do not block JVM exit. Finished threads detach automatically. /// /// ## Example /// /// ```rust /// # use jni::errors; /// # // /// # // Ignore this test without invocation feature, so that simple `cargo test` works /// # #[cfg(feature = "invocation")] /// # fn main() -> errors::StartJvmResult<()> { /// # // /// # use jni::{objects::JValue, Executor, InitArgsBuilder, JavaVM, sys::jint}; /// # use std::sync::Arc; /// # // /// # let jvm_args = InitArgsBuilder::new() /// # .build() /// # .unwrap(); /// # // Create a new VM /// # let jvm = Arc::new(JavaVM::new(jvm_args)?); /// /// let exec = Executor::new(jvm); /// /// let val: jint = exec.with_attached(|env| { /// let x = JValue::from(-10); /// env.call_static_method("java/lang/Math", "abs", "(I)I", &[x])?.i() /// })?; /// /// assert_eq!(val, 10); /// /// # Ok(()) } /// # /// # // This is a stub that gets run instead if the invocation feature is not built /// # #[cfg(not(feature = "invocation"))] /// # fn main() {} /// ``` #[derive(Clone)] pub struct Executor { vm: Arc, } impl Executor { /// Creates new Executor with specified JVM. pub fn new(vm: Arc) -> Self { Self { vm } } /// Executes a provided closure, making sure that the current thread /// is attached to the JVM. Additionally ensures that local object references are freed after /// call. /// /// Allocates a local frame with the specified capacity. pub fn with_attached_capacity(&self, capacity: i32, f: F) -> std::result::Result where F: FnOnce(&mut JNIEnv) -> std::result::Result, E: From, { assert!(capacity > 0, "capacity should be a positive integer"); let mut jni_env = self.vm.attach_current_thread_as_daemon()?; jni_env.with_local_frame(capacity, |jni_env| f(jni_env)) } /// Executes a provided closure, making sure that the current thread /// is attached to the JVM. Additionally ensures that local object references are freed after /// call. /// /// Allocates a local frame with /// [the default capacity](constant.DEFAULT_LOCAL_FRAME_CAPACITY.html). pub fn with_attached(&self, f: F) -> std::result::Result where F: FnOnce(&mut JNIEnv) -> std::result::Result, E: From, { self.with_attached_capacity(DEFAULT_LOCAL_FRAME_CAPACITY, f) } }