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)
}
}