1 use std::sync::Arc; 2 3 use crate::{errors::*, JNIEnv, JavaVM}; 4 5 /// The capacity of local frames, allocated for attached threads by default. Same as the default 6 /// value Hotspot uses when calling native Java methods. 7 pub const DEFAULT_LOCAL_FRAME_CAPACITY: i32 = 32; 8 9 /// Thread attachment manager. It allows to execute closures in attached threads with automatic 10 /// local references management done with `with_local_frame`. It combines the performance benefits 11 /// of permanent attaches whilst removing the risk of local references leaks if used consistently. 12 /// 13 /// Although all locals are freed on closure exit, it might be needed to manually free 14 /// locals _inside_ the closure if an unbounded number of them is created (e.g., in a loop). 15 /// See ["Local Reference Management"](struct.JavaVM.html#local-reference-management) for details. 16 /// 17 /// Threads using the Executor are attached on the first invocation as daemons, 18 /// hence they do not block JVM exit. Finished threads detach automatically. 19 /// 20 /// ## Example 21 /// 22 /// ```rust 23 /// # use jni::errors; 24 /// # // 25 /// # // Ignore this test without invocation feature, so that simple `cargo test` works 26 /// # #[cfg(feature = "invocation")] 27 /// # fn main() -> errors::StartJvmResult<()> { 28 /// # // 29 /// # use jni::{objects::JValue, Executor, InitArgsBuilder, JavaVM, sys::jint}; 30 /// # use std::sync::Arc; 31 /// # // 32 /// # let jvm_args = InitArgsBuilder::new() 33 /// # .build() 34 /// # .unwrap(); 35 /// # // Create a new VM 36 /// # let jvm = Arc::new(JavaVM::new(jvm_args)?); 37 /// 38 /// let exec = Executor::new(jvm); 39 /// 40 /// let val: jint = exec.with_attached(|env| { 41 /// let x = JValue::from(-10); 42 /// env.call_static_method("java/lang/Math", "abs", "(I)I", &[x])?.i() 43 /// })?; 44 /// 45 /// assert_eq!(val, 10); 46 /// 47 /// # Ok(()) } 48 /// # 49 /// # // This is a stub that gets run instead if the invocation feature is not built 50 /// # #[cfg(not(feature = "invocation"))] 51 /// # fn main() {} 52 /// ``` 53 #[derive(Clone)] 54 pub struct Executor { 55 vm: Arc<JavaVM>, 56 } 57 58 impl Executor { 59 /// Creates new Executor with specified JVM. new(vm: Arc<JavaVM>) -> Self60 pub fn new(vm: Arc<JavaVM>) -> Self { 61 Self { vm } 62 } 63 64 /// Executes a provided closure, making sure that the current thread 65 /// is attached to the JVM. Additionally ensures that local object references are freed after 66 /// call. 67 /// 68 /// Allocates a local frame with the specified capacity. with_attached_capacity<F, T, E>(&self, capacity: i32, f: F) -> std::result::Result<T, E> where F: FnOnce(&mut JNIEnv) -> std::result::Result<T, E>, E: From<Error>,69 pub fn with_attached_capacity<F, T, E>(&self, capacity: i32, f: F) -> std::result::Result<T, E> 70 where 71 F: FnOnce(&mut JNIEnv) -> std::result::Result<T, E>, 72 E: From<Error>, 73 { 74 assert!(capacity > 0, "capacity should be a positive integer"); 75 76 let mut jni_env = self.vm.attach_current_thread_as_daemon()?; 77 jni_env.with_local_frame(capacity, |jni_env| f(jni_env)) 78 } 79 80 /// Executes a provided closure, making sure that the current thread 81 /// is attached to the JVM. Additionally ensures that local object references are freed after 82 /// call. 83 /// 84 /// Allocates a local frame with 85 /// [the default capacity](constant.DEFAULT_LOCAL_FRAME_CAPACITY.html). with_attached<F, T, E>(&self, f: F) -> std::result::Result<T, E> where F: FnOnce(&mut JNIEnv) -> std::result::Result<T, E>, E: From<Error>,86 pub fn with_attached<F, T, E>(&self, f: F) -> std::result::Result<T, E> 87 where 88 F: FnOnce(&mut JNIEnv) -> std::result::Result<T, E>, 89 E: From<Error>, 90 { 91 self.with_attached_capacity(DEFAULT_LOCAL_FRAME_CAPACITY, f) 92 } 93 } 94