blob: fefe7723476c53fcf48f7f417b6314147357a190 [file] [log] [blame]
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<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, 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>,
{
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<F, T, E>(&self, f: F) -> std::result::Result<T, E>
where
F: FnOnce(&mut JNIEnv) -> std::result::Result<T, E>,
E: From<Error>,
{
self.with_attached_capacity(DEFAULT_LOCAL_FRAME_CAPACITY, f)
}
}