• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::{
2     cell::RefCell,
3     ops::Deref,
4     ptr,
5     sync::atomic::{AtomicUsize, Ordering},
6     thread::current,
7 };
8 
9 use log::{debug, error};
10 
11 use crate::{errors::*, sys, JNIEnv};
12 
13 #[cfg(feature = "invocation")]
14 use crate::InitArgs;
15 
16 /// The Java VM, providing [Invocation API][invocation-api] support.
17 ///
18 /// The JavaVM can be obtained either via [`JNIEnv#get_java_vm`][get-vm] in an already attached
19 /// thread, or it can be [launched](#launching-jvm-from-rust) from Rust via `JavaVM#new`.
20 ///
21 /// ## Attaching Native Threads
22 ///
23 /// A native thread must «attach» itself to be able to call Java methods outside of a native Java
24 /// method. This library provides two modes of attachment, each ensuring the thread is promptly
25 /// detached:
26 /// * A scoped attachment with [`attach_current_thread`][act].
27 ///   The thread will automatically detach itself once the returned guard is dropped.
28 /// * A permanent attachment with [`attach_current_thread_permanently`][actp]
29 ///   or [`attach_current_thread_as_daemon`][actd].
30 ///   The thread will automatically detach itself before it terminates.
31 ///
32 /// As attachment and detachment of a thread is an expensive operation, the scoped attachment
33 /// shall be used if happens infrequently. If you have an undefined scope where you need
34 /// to use `JNIEnv` and cannot keep the `AttachGuard`, consider attaching the thread
35 /// permanently.
36 ///
37 /// ### Local Reference Management
38 ///
39 /// Remember that the native thread attached to the VM **must** manage the local references
40 /// properly, i.e., do not allocate an excessive number of references and release them promptly
41 /// when they are no longer needed to enable the GC to collect them. A common approach is to use
42 /// an appropriately-sized local frame for larger code fragments
43 /// (see [`with_local_frame`](struct.JNIEnv.html#method.with_local_frame) and [Executor](#executor))
44 /// and [auto locals](struct.JNIEnv.html#method.auto_local) in loops.
45 ///
46 /// See also the [JNI specification][spec-references] for details on referencing Java objects.
47 ///
48 /// ### Executor
49 ///
50 /// Jni-rs provides an [`Executor`](struct.Executor.html) — a helper struct that allows to
51 /// execute a closure with `JNIEnv`. It combines the performance benefits of permanent attaches
52 /// *and* automatic local reference management. Prefer it to manual permanent attaches if
53 /// they happen in various parts of the code to reduce the burden of local reference management.
54 ///
55 /// ## Launching JVM from Rust
56 ///
57 /// To [launch][launch-vm] a JVM from a native process, enable the `invocation` feature
58 /// in the Cargo.toml:
59 ///
60 /// ```toml
61 /// jni = { version = "0.19.0", features = ["invocation"] }
62 /// ```
63 ///
64 /// The application will require linking to the dynamic `jvm` library, which is distributed
65 /// with the JVM, and allow to use `JavaVM#new`:
66 ///
67 /// ```rust
68 /// # use jni::errors;
69 /// # //
70 /// # fn main() -> errors::Result<()> {
71 /// # // Ignore this test without invocation feature, so that simple `cargo test` works
72 /// # #[cfg(feature = "invocation")] {
73 /// # //
74 /// # use jni::{AttachGuard, objects::JValue, InitArgsBuilder, JNIEnv, JNIVersion, JavaVM, sys::jint};
75 /// # //
76 /// // Build the VM properties
77 /// let jvm_args = InitArgsBuilder::new()
78 ///           // Pass the JNI API version (default is 8)
79 ///           .version(JNIVersion::V8)
80 ///           // You can additionally pass any JVM options (standard, like a system property,
81 ///           // or VM-specific).
82 ///           // Here we enable some extra JNI checks useful during development
83 ///           .option("-Xcheck:jni")
84 ///           .build()
85 ///           .unwrap();
86 ///
87 /// // Create a new VM
88 /// let jvm = JavaVM::new(jvm_args)?;
89 ///
90 /// // Attach the current thread to call into Java — see extra options in
91 /// // "Attaching Native Threads" section.
92 /// //
93 /// // This method returns the guard that will detach the current thread when dropped,
94 /// // also freeing any local references created in it
95 /// let env = jvm.attach_current_thread()?;
96 ///
97 /// // Call Java Math#abs(-10)
98 /// let x = JValue::from(-10);
99 /// let val: jint = env.call_static_method("java/lang/Math", "abs", "(I)I", &[x])?
100 ///   .i()?;
101 ///
102 /// assert_eq!(val, 10);
103 ///
104 /// # }
105 /// # Ok(()) }
106 /// ```
107 ///
108 /// During build time, the JVM installation path is determined:
109 /// 1. By `JAVA_HOME` environment variable, if it is set.
110 /// 2. Otherwise — from `java` output.
111 ///
112 /// It is recommended to set `JAVA_HOME` to have reproducible builds,
113 /// especially, in case of multiple VMs installed.
114 ///
115 /// At application run time, you must specify the path
116 /// to the `jvm` library so that the loader can locate it.
117 /// * On **Windows**, append the path to `jvm.dll` to `PATH` environment variable.
118 /// * On **MacOS**, append the path to `libjvm.dylib` to `LD_LIBRARY_PATH` environment variable.
119 /// * On **Linux**, append the path to `libjvm.so` to `LD_LIBRARY_PATH` environment variable.
120 ///
121 /// The exact relative path to `jvm` library is version-specific.
122 ///
123 /// For more information on linking — see documentation
124 /// in [build.rs](https://github.com/jni-rs/jni-rs/tree/master/build.rs).
125 ///
126 /// [invocation-api]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/invocation.html
127 /// [get-vm]: struct.JNIEnv.html#method.get_java_vm
128 /// [launch-vm]: struct.JavaVM.html#method.new
129 /// [act]: struct.JavaVM.html#method.attach_current_thread
130 /// [actp]: struct.JavaVM.html#method.attach_current_thread_permanently
131 /// [actd]: struct.JavaVM.html#method.attach_current_thread_as_daemon
132 /// [spec-references]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/design.html#referencing-java-objects
133 #[repr(transparent)]
134 pub struct JavaVM(*mut sys::JavaVM);
135 
136 unsafe impl Send for JavaVM {}
137 unsafe impl Sync for JavaVM {}
138 
139 impl JavaVM {
140     /// Launch a new JavaVM using the provided init args.
141     ///
142     /// Unlike original JNI API, the main thread (the thread from which this method is called) will
143     /// not be attached to JVM. You must explicitly use `attach_current_thread…` methods (refer
144     /// to [Attaching Native Threads section](#attaching-native-threads)).
145     ///
146     /// *This API requires "invocation" feature to be enabled,
147     /// see ["Launching JVM from Rust"](struct.JavaVM.html#launching-jvm-from-rust).*
148     #[cfg(feature = "invocation")]
new(args: InitArgs) -> Result<Self>149     pub fn new(args: InitArgs) -> Result<Self> {
150         use std::os::raw::c_void;
151 
152         let mut ptr: *mut sys::JavaVM = ::std::ptr::null_mut();
153         let mut env: *mut sys::JNIEnv = ::std::ptr::null_mut();
154 
155         unsafe {
156             jni_error_code_to_result(sys::JNI_CreateJavaVM(
157                 &mut ptr as *mut _,
158                 &mut env as *mut *mut sys::JNIEnv as *mut *mut c_void,
159                 args.inner_ptr(),
160             ))?;
161 
162             let vm = Self::from_raw(ptr)?;
163             java_vm_unchecked!(vm.0, DetachCurrentThread);
164 
165             Ok(vm)
166         }
167     }
168 
169     /// Create a JavaVM from a raw pointer.
170     ///
171     /// # Safety
172     ///
173     /// Expects a valid pointer retrieved from the `JNI_CreateJavaVM` JNI function. Only does null check.
from_raw(ptr: *mut sys::JavaVM) -> Result<Self>174     pub unsafe fn from_raw(ptr: *mut sys::JavaVM) -> Result<Self> {
175         non_null!(ptr, "from_raw ptr argument");
176         Ok(JavaVM(ptr))
177     }
178 
179     /// Returns underlying `sys::JavaVM` interface.
get_java_vm_pointer(&self) -> *mut sys::JavaVM180     pub fn get_java_vm_pointer(&self) -> *mut sys::JavaVM {
181         self.0
182     }
183 
184     /// Attaches the current thread to the JVM. Calling this for a thread that is already attached
185     /// is a no-op.
186     ///
187     /// The thread will detach itself automatically when it exits.
188     ///
189     /// Attached threads [block JVM exit][block]. If it is not desirable — consider using
190     /// [`attach_current_thread_as_daemon`][attach-as-daemon].
191     ///
192     /// [block]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/invocation.html#unloading-the-vm
193     /// [attach-as-daemon]: struct.JavaVM.html#method.attach_current_thread_as_daemon
attach_current_thread_permanently(&self) -> Result<JNIEnv>194     pub fn attach_current_thread_permanently(&self) -> Result<JNIEnv> {
195         match self.get_env() {
196             Ok(env) => Ok(env),
197             Err(_) => self.attach_current_thread_impl(ThreadType::Normal),
198         }
199     }
200 
201     /// Attaches the current thread to the Java VM. The returned `AttachGuard`
202     /// can be dereferenced to a `JNIEnv` and automatically detaches the thread
203     /// when dropped. Calling this in a thread that is already attached is a no-op, and
204     /// will neither change its daemon status nor prematurely detach it.
205     ///
206     /// Attached threads [block JVM exit][block].
207     ///
208     /// Attaching and detaching a thread is an expensive operation. If you use it frequently
209     /// in the same threads, consider either [attaching them permanently][attach-as-daemon],
210     /// or, if the scope where you need the `JNIEnv` is well-defined, keeping the returned guard.
211     ///
212     /// [block]: https://docs.oracle.com/en/java/javase/12/docs/specs/jni/invocation.html#unloading-the-vm
213     /// [attach-as-daemon]: struct.JavaVM.html#method.attach_current_thread_as_daemon
attach_current_thread(&self) -> Result<AttachGuard>214     pub fn attach_current_thread(&self) -> Result<AttachGuard> {
215         match self.get_env() {
216             Ok(env) => Ok(AttachGuard::new_nested(env)),
217             Err(_) => {
218                 let env = self.attach_current_thread_impl(ThreadType::Normal)?;
219                 Ok(AttachGuard::new(env))
220             }
221         }
222     }
223 
224     /// Detaches current thread from the JVM. This operation is _rarely_ appropriate to use,
225     /// because the attachment methods [ensure](#attaching-native-threads) that the thread is
226     /// promptly detached.
227     ///
228     /// Detaching a non-attached thread is a no-op.
229     ///
230     /// __Any existing `JNIEnv`s and `AttachGuard`s created in the calling thread
231     /// will be invalidated after this method completes. It is the__ caller’s __responsibility
232     /// to ensure that no JNI calls are subsequently performed on these objects.__
233     /// Failure to do so will result in unspecified errors, possibly, the process crash.
234     ///
235     /// Given some care is exercised, this method can be used to detach permanently attached
236     /// threads _before_ they exit (when automatic detachment occurs). However, it is
237     /// never appropriate to use it with the scoped attachment (`attach_current_thread`).
238     // This method is hidden because it is almost never needed and its use requires some
239     // extra care. Its status might be reconsidered if we learn of any use cases that require it.
240     #[doc(hidden)]
detach_current_thread(&self)241     pub fn detach_current_thread(&self) {
242         InternalAttachGuard::clear_tls();
243     }
244 
245     /// Attaches the current thread to the Java VM as a _daemon_. Calling this in a thread
246     /// that is already attached is a no-op, and will not change its status to a daemon thread.
247     ///
248     /// The thread will detach itself automatically when it exits.
attach_current_thread_as_daemon(&self) -> Result<JNIEnv>249     pub fn attach_current_thread_as_daemon(&self) -> Result<JNIEnv> {
250         match self.get_env() {
251             Ok(env) => Ok(env),
252             Err(_) => self.attach_current_thread_impl(ThreadType::Daemon),
253         }
254     }
255 
256     /// Returns the current number of threads attached to the JVM.
257     ///
258     /// This method is provided mostly for diagnostic purposes.
threads_attached(&self) -> usize259     pub fn threads_attached(&self) -> usize {
260         ATTACHED_THREADS.load(Ordering::SeqCst)
261     }
262 
263     /// Get the `JNIEnv` associated with the current thread, or
264     /// `ErrorKind::Detached`
265     /// if the current thread is not attached to the java VM.
get_env(&self) -> Result<JNIEnv>266     pub fn get_env(&self) -> Result<JNIEnv> {
267         let mut ptr = ptr::null_mut();
268         unsafe {
269             let res = java_vm_unchecked!(self.0, GetEnv, &mut ptr, sys::JNI_VERSION_1_1);
270             jni_error_code_to_result(res)?;
271 
272             JNIEnv::from_raw(ptr as *mut sys::JNIEnv)
273         }
274     }
275 
276     /// Creates `InternalAttachGuard` and attaches current thread.
attach_current_thread_impl(&self, thread_type: ThreadType) -> Result<JNIEnv>277     fn attach_current_thread_impl(&self, thread_type: ThreadType) -> Result<JNIEnv> {
278         let guard = InternalAttachGuard::new(self.get_java_vm_pointer());
279         let env_ptr = unsafe {
280             if thread_type == ThreadType::Daemon {
281                 guard.attach_current_thread_as_daemon()?
282             } else {
283                 guard.attach_current_thread()?
284             }
285         };
286 
287         InternalAttachGuard::fill_tls(guard);
288 
289         unsafe { JNIEnv::from_raw(env_ptr as *mut sys::JNIEnv) }
290     }
291 }
292 
293 thread_local! {
294     static THREAD_ATTACH_GUARD: RefCell<Option<InternalAttachGuard>> = RefCell::new(None)
295 }
296 
297 static ATTACHED_THREADS: AtomicUsize = AtomicUsize::new(0);
298 
299 /// A RAII implementation of scoped guard which detaches the current thread
300 /// when dropped. The attached `JNIEnv` can be accessed through this guard
301 /// via its `Deref` implementation.
302 pub struct AttachGuard<'a> {
303     env: JNIEnv<'a>,
304     should_detach: bool,
305 }
306 
307 impl<'a> AttachGuard<'a> {
308     /// AttachGuard created with this method will detach current thread on drop
new(env: JNIEnv<'a>) -> Self309     fn new(env: JNIEnv<'a>) -> Self {
310         Self {
311             env,
312             should_detach: true,
313         }
314     }
315 
316     /// AttachGuard created with this method will not detach current thread on drop, which is
317     /// the case for nested attaches.
new_nested(env: JNIEnv<'a>) -> Self318     fn new_nested(env: JNIEnv<'a>) -> Self {
319         Self {
320             env,
321             should_detach: false,
322         }
323     }
324 }
325 
326 impl<'a> Deref for AttachGuard<'a> {
327     type Target = JNIEnv<'a>;
328 
deref(&self) -> &Self::Target329     fn deref(&self) -> &Self::Target {
330         &self.env
331     }
332 }
333 
334 impl<'a> Drop for AttachGuard<'a> {
drop(&mut self)335     fn drop(&mut self) {
336         if self.should_detach {
337             InternalAttachGuard::clear_tls();
338         }
339     }
340 }
341 
342 #[derive(PartialEq)]
343 enum ThreadType {
344     Normal,
345     Daemon,
346 }
347 
348 #[derive(Debug)]
349 struct InternalAttachGuard {
350     java_vm: *mut sys::JavaVM,
351 }
352 
353 impl InternalAttachGuard {
new(java_vm: *mut sys::JavaVM) -> Self354     fn new(java_vm: *mut sys::JavaVM) -> Self {
355         Self { java_vm }
356     }
357 
358     /// Stores guard in thread local storage.
fill_tls(guard: InternalAttachGuard)359     fn fill_tls(guard: InternalAttachGuard) {
360         THREAD_ATTACH_GUARD.with(move |f| {
361             *f.borrow_mut() = Some(guard);
362         });
363     }
364 
365     /// Clears thread local storage, dropping the InternalAttachGuard and causing detach of
366     /// the current thread.
clear_tls()367     fn clear_tls() {
368         THREAD_ATTACH_GUARD.with(move |f| {
369             *f.borrow_mut() = None;
370         });
371     }
372 
attach_current_thread(&self) -> Result<*mut sys::JNIEnv>373     unsafe fn attach_current_thread(&self) -> Result<*mut sys::JNIEnv> {
374         let mut env_ptr = ptr::null_mut();
375         let res = java_vm_unchecked!(
376             self.java_vm,
377             AttachCurrentThread,
378             &mut env_ptr,
379             ptr::null_mut()
380         );
381         jni_error_code_to_result(res)?;
382 
383         ATTACHED_THREADS.fetch_add(1, Ordering::SeqCst);
384 
385         debug!(
386             "Attached thread {} ({:?}). {} threads attached",
387             current().name().unwrap_or_default(),
388             current().id(),
389             ATTACHED_THREADS.load(Ordering::SeqCst)
390         );
391 
392         Ok(env_ptr as *mut sys::JNIEnv)
393     }
394 
attach_current_thread_as_daemon(&self) -> Result<*mut sys::JNIEnv>395     unsafe fn attach_current_thread_as_daemon(&self) -> Result<*mut sys::JNIEnv> {
396         let mut env_ptr = ptr::null_mut();
397         let res = java_vm_unchecked!(
398             self.java_vm,
399             AttachCurrentThreadAsDaemon,
400             &mut env_ptr,
401             ptr::null_mut()
402         );
403         jni_error_code_to_result(res)?;
404 
405         ATTACHED_THREADS.fetch_add(1, Ordering::SeqCst);
406 
407         debug!(
408             "Attached daemon thread {} ({:?}). {} threads attached",
409             current().name().unwrap_or_default(),
410             current().id(),
411             ATTACHED_THREADS.load(Ordering::SeqCst)
412         );
413 
414         Ok(env_ptr as *mut sys::JNIEnv)
415     }
416 
detach(&mut self) -> Result<()>417     fn detach(&mut self) -> Result<()> {
418         unsafe {
419             java_vm_unchecked!(self.java_vm, DetachCurrentThread);
420         }
421         ATTACHED_THREADS.fetch_sub(1, Ordering::SeqCst);
422         debug!(
423             "Detached thread {} ({:?}). {} threads remain attached",
424             current().name().unwrap_or_default(),
425             current().id(),
426             ATTACHED_THREADS.load(Ordering::SeqCst)
427         );
428 
429         Ok(())
430     }
431 }
432 
433 impl Drop for InternalAttachGuard {
drop(&mut self)434     fn drop(&mut self) {
435         if let Err(e) = self.detach() {
436             error!(
437                 "Error detaching current thread: {:#?}\nThread {} id={:?}",
438                 e,
439                 current().name().unwrap_or_default(),
440                 current().id(),
441             );
442         }
443     }
444 }
445