This is an automated email from the ASF dual-hosted git repository.
tison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 2b89a68bd fix(bindings/java): Detach current thread while thread drop
(#7000)
2b89a68bd is described below
commit 2b89a68bde1d1703945d02bebb4bc2b812ecb242
Author: Xuanwo <[email protected]>
AuthorDate: Sun Dec 14 09:04:05 2025 +0800
fix(bindings/java): Detach current thread while thread drop (#7000)
Signed-off-by: Xuanwo <[email protected]>
Co-authored-by: tison <[email protected]>
---
bindings/java/src/executor.rs | 41 +++++++++++++++++++++++++++++++----------
1 file changed, 31 insertions(+), 10 deletions(-)
diff --git a/bindings/java/src/executor.rs b/bindings/java/src/executor.rs
index 0db816884..af0dd13f5 100644
--- a/bindings/java/src/executor.rs
+++ b/bindings/java/src/executor.rs
@@ -19,8 +19,8 @@ use std::cell::RefCell;
use std::ffi::c_void;
use std::future::Future;
use std::num::NonZeroUsize;
-use std::sync::OnceLock;
use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::{Arc, OnceLock};
use std::thread::available_parallelism;
use jni::JNIEnv;
@@ -116,7 +116,7 @@ pub unsafe extern "system" fn
Java_org_apache_opendal_AsyncExecutor_disposeInter
}
pub(crate) fn make_tokio_executor(env: &mut JNIEnv, cores: usize) ->
Result<Executor> {
- let vm = env.get_java_vm().expect("JavaVM must be available");
+ let vm = Arc::new(env.get_java_vm().expect("JavaVM must be available"));
let counter = AtomicUsize::new(0);
let executor = tokio::runtime::Builder::new_multi_thread()
.worker_threads(cores)
@@ -124,16 +124,37 @@ pub(crate) fn make_tokio_executor(env: &mut JNIEnv,
cores: usize) -> Result<Exec
let id = counter.fetch_add(1, Ordering::SeqCst);
format!("opendal-tokio-worker-{id}")
})
- .on_thread_start(move || {
- ENV.with(|cell| {
- let mut env = vm
- .attach_current_thread_as_daemon()
- .expect("attach thread must succeed");
+ .on_thread_start({
+ let vm = vm.clone();
+ move || {
+ ENV.with(|cell| {
+ let mut env = vm
+ .attach_current_thread_permanently()
+ .expect("attach thread must succeed");
+
+ set_current_thread_name(&mut env)
+ .expect("current thread name has been set above");
+
+ *cell.borrow_mut() = Some(env.get_raw());
+ })
+ }
+ })
+ .on_thread_stop(move || {
+ // Typically, the thread attached to the JVM will be detached
automatically when the thread exits
+ // and the corresponding thread-local AttachGuard is dropped.
+ //
+ // However, there are some edge cases on Windows that may lead to
deadlocks. To mitigate this,
+ // we explicitly detach the thread here.
+ //
+ // See https://github.com/apache/opendal/issues/6869 and
https://github.com/jni-rs/jni-rs/issues/701
+ // for more details.
- set_current_thread_name(&mut env).expect("current thread name
has been set above");
+ ENV.with(|cell| {
+ *cell.borrow_mut() = None;
+ });
- *cell.borrow_mut() = Some(env.get_raw());
- })
+ // SAFETY: JNIEnv is unset above and we do not use AttachGuard
anywhere.
+ unsafe { vm.detach_current_thread() };
})
.enable_all()
.build()