• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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