Skip to content

Commit d671991

Browse files
authored
Merge pull request #3 from wuwbobo2021/receiver
Fixes possible crash on thread exit
2 parents 1ab34b9 + 9404a3d commit d671991

File tree

1 file changed

+47
-6
lines changed

1 file changed

+47
-6
lines changed

java-spaghetti/src/vm.rs

+47-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::cell::{Cell, OnceCell};
12
use std::ptr::null_mut;
23

34
use jni_sys::*;
@@ -37,17 +38,57 @@ impl VM {
3738
F: for<'env> FnOnce(Env<'env>) -> R,
3839
{
3940
let mut env = null_mut();
40-
match unsafe { ((**self.0).v1_2.GetEnv)(self.0, &mut env, JNI_VERSION_1_2) } {
41-
JNI_OK => callback(unsafe { Env::from_raw(env as _) }),
42-
JNI_EDETACHED => match unsafe { ((**self.0).v1_2.AttachCurrentThread)(self.0, &mut env, null_mut()) } {
43-
JNI_OK => callback(unsafe { Env::from_raw(env as _) }),
44-
unexpected => panic!("AttachCurrentThread returned unknown error: {}", unexpected),
45-
},
41+
let just_attached = match unsafe { ((**self.0).v1_2.GetEnv)(self.0, &mut env, JNI_VERSION_1_2) } {
42+
JNI_OK => false,
43+
JNI_EDETACHED => {
44+
let ret = unsafe { ((**self.0).v1_2.AttachCurrentThread)(self.0, &mut env, null_mut()) };
45+
if ret != JNI_OK {
46+
panic!("AttachCurrentThread returned unknown error: {}", ret)
47+
}
48+
if !get_thread_exit_flag() {
49+
set_thread_attach_flag(self.0);
50+
}
51+
true
52+
}
4653
JNI_EVERSION => panic!("GetEnv returned JNI_EVERSION"),
4754
unexpected => panic!("GetEnv returned unknown error: {}", unexpected),
55+
};
56+
57+
let result = callback(unsafe { Env::from_raw(env as _) });
58+
59+
if just_attached && get_thread_exit_flag() {
60+
// this is needed in case of `with_env` is used on dropping some thread-local instance.
61+
unsafe { ((**self.0).v1_2.DetachCurrentThread)(self.0) };
4862
}
63+
64+
result
4965
}
5066
}
5167

5268
unsafe impl Send for VM {}
5369
unsafe impl Sync for VM {}
70+
71+
thread_local! {
72+
static THREAD_ATTACH_FLAG: Cell<Option<AttachFlag>> = const { Cell::new(None) };
73+
static THREAD_EXIT_FLAG: OnceCell<()> = const { OnceCell::new() };
74+
}
75+
76+
struct AttachFlag {
77+
raw_vm: *mut JavaVM,
78+
}
79+
80+
impl Drop for AttachFlag {
81+
fn drop(&mut self) {
82+
// avoids the fatal error "Native thread exiting without having called DetachCurrentThread"
83+
unsafe { ((**self.raw_vm).v1_2.DetachCurrentThread)(self.raw_vm) };
84+
let _ = THREAD_EXIT_FLAG.with(|flag| flag.set(()));
85+
}
86+
}
87+
88+
fn set_thread_attach_flag(raw_vm: *mut JavaVM) {
89+
THREAD_ATTACH_FLAG.replace(Some(AttachFlag { raw_vm }));
90+
}
91+
92+
fn get_thread_exit_flag() -> bool {
93+
THREAD_EXIT_FLAG.with(|flag| flag.get().is_some())
94+
}

0 commit comments

Comments
 (0)