1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
use std::sync::Arc; use crate::{errors::*, objects::JObject, 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; /// # // /// # fn main() -> errors::Result<()> { /// # // Ignore this test without invocation feature, so that simple `cargo test` works /// # #[cfg(feature = "invocation")] { /// # // /// # 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); /// let val: jint = env.call_static_method("java/lang/Math", "abs", "(I)I", &[x])? /// .i()?; /// Ok(val) /// })?; /// /// assert_eq!(val, 10); /// /// # } /// # Ok(()) } /// ``` #[derive(Clone)] pub struct Executor { vm: Arc<JavaVM>, } impl Executor { /// Creates new Executor with specified JVM. pub fn new(vm: Arc<JavaVM>) -> 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<F, R>(&self, capacity: i32, f: F) -> Result<R> where F: FnOnce(&JNIEnv) -> Result<R>, { assert!(capacity > 0, "capacity should be a positive integer"); let jni_env = self.vm.attach_current_thread_as_daemon()?; let mut result = None; jni_env.with_local_frame(capacity, || { result = Some(f(&jni_env)); Ok(JObject::null()) })?; result.expect("The result should be Some or this line shouldn't be reached") } /// 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<F, R>(&self, f: F) -> Result<R> where F: FnOnce(&JNIEnv) -> Result<R>, { self.with_attached_capacity(DEFAULT_LOCAL_FRAME_CAPACITY, f) } }