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