1 use std::sync::Arc; 2 3 use crate::{errors::*, objects::JObject, 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 /// # fn main() -> errors::Result<()> { 26 /// # // Ignore this test without invocation feature, so that simple `cargo test` works 27 /// # #[cfg(feature = "invocation")] { 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 /// let val: jint = env.call_static_method("java/lang/Math", "abs", "(I)I", &[x])? 43 /// .i()?; 44 /// Ok(val) 45 /// })?; 46 /// 47 /// assert_eq!(val, 10); 48 /// 49 /// # } 50 /// # Ok(()) } 51 /// ``` 52 #[derive(Clone)] 53 pub struct Executor { 54 vm: Arc<JavaVM>, 55 } 56 57 impl Executor { 58 /// Creates new Executor with specified JVM. new(vm: Arc<JavaVM>) -> Self59 pub fn new(vm: Arc<JavaVM>) -> Self { 60 Self { vm } 61 } 62 63 /// Executes a provided closure, making sure that the current thread 64 /// is attached to the JVM. Additionally ensures that local object references are freed after 65 /// call. 66 /// 67 /// Allocates a local frame with the specified capacity. with_attached_capacity<F, R>(&self, capacity: i32, f: F) -> Result<R> where F: FnOnce(&JNIEnv) -> Result<R>,68 pub fn with_attached_capacity<F, R>(&self, capacity: i32, f: F) -> Result<R> 69 where 70 F: FnOnce(&JNIEnv) -> Result<R>, 71 { 72 assert!(capacity > 0, "capacity should be a positive integer"); 73 74 let jni_env = self.vm.attach_current_thread_as_daemon()?; 75 let mut result = None; 76 jni_env.with_local_frame(capacity, || { 77 result = Some(f(&jni_env)); 78 Ok(JObject::null()) 79 })?; 80 81 result.expect("The result should be Some or this line shouldn't be reached") 82 } 83 84 /// Executes a provided closure, making sure that the current thread 85 /// is attached to the JVM. Additionally ensures that local object references are freed after 86 /// call. 87 /// 88 /// Allocates a local frame with 89 /// [the default capacity](constant.DEFAULT_LOCAL_FRAME_CAPACITY.html). with_attached<F, R>(&self, f: F) -> Result<R> where F: FnOnce(&JNIEnv) -> Result<R>,90 pub fn with_attached<F, R>(&self, f: F) -> Result<R> 91 where 92 F: FnOnce(&JNIEnv) -> Result<R>, 93 { 94 self.with_attached_capacity(DEFAULT_LOCAL_FRAME_CAPACITY, f) 95 } 96 } 97