fix(jni): stopServer 改为后台线程释放 Runtime,避免阻塞 JNI 调用线程
- 新增 take_handle: 转移 ServerHandle 所有权,替代 handle_ref+destroy_handle 两步调用 - 新增 shutdown_and_destroy_in_background: 先发关闭信号,再将整个 ServerHandle(含 tokio Runtime)移入专用后台线程 drop,避免 Runtime::drop 阻塞调用 stopServer 的 Java 线程 - jni_shutdown_and_release 改用 take_handle + shutdown_and_destroy_in_background - 保留 stop/destroy_handle 供扩展,标注 #[allow(dead_code)] 并补充注释 Made-with: Cursor
This commit is contained in:
+3
-5
@@ -1,6 +1,6 @@
|
||||
use crate::jni_bindings::callbacks::register_callbacks;
|
||||
use crate::jni_bindings::env::JavaCallback;
|
||||
use crate::jni_bindings::server::{destroy_handle, handle_ref, into_handle_ptr, ServerHandle};
|
||||
use crate::jni_bindings::server::{handle_ref, into_handle_ptr, take_handle, ServerHandle};
|
||||
use crate::jni_bindings::types::java_to_dht_options_or_default;
|
||||
use jni::JNIEnv;
|
||||
use jni::objects::{JClass, JObject};
|
||||
@@ -102,14 +102,12 @@ fn jni_shutdown_and_release(env: &mut JNIEnv, handle: jlong) {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
match handle_ref(handle) {
|
||||
Some(h) => h.stop(),
|
||||
match take_handle(handle) {
|
||||
Some(h) => h.shutdown_and_destroy_in_background(),
|
||||
None => {
|
||||
let _ = env.throw_new("java/lang/IllegalArgumentException", "无效的服务器句柄");
|
||||
return;
|
||||
}
|
||||
}
|
||||
destroy_handle(handle);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+36
-1
@@ -34,11 +34,31 @@ impl ServerHandle {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 发送关闭信号(非阻塞)。
|
||||
/// 发送关闭信号(非阻塞)。仅供单独使用,通常应调用 `shutdown_and_destroy_in_background`。
|
||||
#[allow(dead_code)]
|
||||
pub fn stop(&self) {
|
||||
self.server.shutdown();
|
||||
}
|
||||
|
||||
/// 在专用后台线程中 drop 整个 handle(含 Runtime),
|
||||
/// 避免 Runtime::drop 阻塞 JNI 调用线程。
|
||||
/// 先发关闭信号,然后把 handle 所有权移入后台线程;
|
||||
/// 后台线程等待 tokio runtime 中所有任务退出后统一释放资源。
|
||||
pub fn shutdown_and_destroy_in_background(self) {
|
||||
self.server.shutdown();
|
||||
if let Err(e) = std::thread::Builder::new()
|
||||
.name("dht-jni-shutdown".to_owned())
|
||||
.spawn(move || {
|
||||
// drop(self) 在此发生:Runtime::drop 阻塞等待所有 tokio 任务退出
|
||||
// 但此时已在独立线程,不会卡 JNI 调用线程
|
||||
drop(self);
|
||||
})
|
||||
{
|
||||
// 线程创建失败(极罕见),fallback 在当前线程同步释放,保底不泄漏
|
||||
log::error!("dht-jni-shutdown 线程创建失败,在当前线程同步释放: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
/// 返回节点池大小。
|
||||
pub fn node_pool_size(&self) -> usize {
|
||||
self.server.get_node_pool_size()
|
||||
@@ -65,10 +85,25 @@ pub unsafe fn handle_ref<'a>(ptr: i64) -> Option<&'a ServerHandle> {
|
||||
Some(unsafe { &*(ptr as *const ServerHandle) })
|
||||
}
|
||||
|
||||
/// 消费句柄:从裸指针重建 Box 并返回 `ServerHandle` 所有权。
|
||||
/// 调用后 Java 侧不得再使用该句柄。
|
||||
///
|
||||
/// # Safety
|
||||
/// 只能调用一次;ptr 必须是由 `into_handle_ptr` 生成的合法指针。
|
||||
pub unsafe fn take_handle(ptr: i64) -> Option<ServerHandle> {
|
||||
if ptr == 0 {
|
||||
return None;
|
||||
}
|
||||
Some(*unsafe { Box::from_raw(ptr as *mut ServerHandle) })
|
||||
}
|
||||
|
||||
/// 消费句柄:从裸指针重建 Box 并 drop,释放所有资源(包括 runtime)。
|
||||
/// 注意:会阻塞当前线程直到 Runtime 中所有任务退出。
|
||||
/// 通常应优先使用 `take_handle` + `shutdown_and_destroy_in_background`。
|
||||
///
|
||||
/// # Safety
|
||||
/// 只能调用一次,调用后 Java 侧不得再使用该句柄。
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn destroy_handle(ptr: i64) {
|
||||
if ptr != 0 {
|
||||
drop(unsafe { Box::from_raw(ptr as *mut ServerHandle) });
|
||||
|
||||
Reference in New Issue
Block a user