Skip to content

Commit 8fd50fd

Browse files
committed
Abort when a GC thread panics.
Previously the behavior is printing backtrace and terminating the GC thread itself instead of the entire process, and the mutator threads will be hanging there waiting for GC to finish.
1 parent 7e6f35e commit 8fd50fd

File tree

3 files changed

+78
-2
lines changed

3 files changed

+78
-2
lines changed

mmtk/src/api.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ pub extern "C" fn mmtk_init_binding(
7979
binding_options: *const RubyBindingOptions,
8080
upcalls: *const abi::RubyUpcalls,
8181
) {
82+
crate::set_panic_hook();
83+
8284
let builder = unsafe { Box::from_raw(builder) };
8385
let binding_options = unsafe { &*binding_options };
8486
let mmtk_boxed = mmtk_init(&builder);

mmtk/src/collection.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ impl Collection<Ruby> for VMCollection {
3535
.name("MMTk Controller Thread".to_string())
3636
.spawn(move || {
3737
debug!("Hello! This is MMTk Controller Thread running!");
38+
crate::register_gc_thread(thread::current().id());
3839
let ptr_controller = &mut *controller as *mut GCController<Ruby>;
3940
let gc_thread_tls =
4041
Box::into_raw(Box::new(GCThreadTLS::for_controller(ptr_controller)));
@@ -43,7 +44,12 @@ impl Collection<Ruby> for VMCollection {
4344
mmtk(),
4445
GCThreadTLS::to_vwt(gc_thread_tls),
4546
&mut controller,
46-
)
47+
);
48+
49+
// Currently the MMTk controller thread should run forever.
50+
// This is an unlikely event, but we log it anyway.
51+
warn!("The MMTk Controller Thread is quitting!");
52+
crate::unregister_gc_thread(thread::current().id());
4753
})
4854
.unwrap();
4955
}
@@ -52,6 +58,7 @@ impl Collection<Ruby> for VMCollection {
5258
.name("MMTk Worker Thread".to_string())
5359
.spawn(move || {
5460
debug!("Hello! This is MMTk Worker Thread running!");
61+
crate::register_gc_thread(thread::current().id());
5562
let ptr_worker = &mut *worker as *mut GCWorker<Ruby>;
5663
let gc_thread_tls =
5764
Box::into_raw(Box::new(GCThreadTLS::for_worker(ptr_worker)));
@@ -60,7 +67,12 @@ impl Collection<Ruby> for VMCollection {
6067
mmtk(),
6168
GCThreadTLS::to_vwt(gc_thread_tls),
6269
&mut worker,
63-
)
70+
);
71+
72+
// Currently all MMTk worker threads should run forever.
73+
// This is an unlikely event, but we log it anyway.
74+
warn!("An MMTk Worker Thread is quitting!");
75+
crate::unregister_gc_thread(thread::current().id());
6476
})
6577
.unwrap();
6678
}

mmtk/src/lib.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ extern crate mmtk;
33
#[macro_use]
44
extern crate log;
55

6+
use std::collections::HashSet;
7+
use std::panic::PanicInfo;
8+
use std::sync::Mutex;
9+
use std::thread::ThreadId;
10+
611
use abi::RubyUpcalls;
712
use binding::{RubyBinding, RubyBindingFast};
813
use mmtk::vm::edge_shape::{SimpleEdge, UnimplementedMemorySlice};
@@ -65,3 +70,60 @@ pub fn mmtk() -> &'static MMTK<Ruby> {
6570
pub fn upcalls() -> &'static RubyUpcalls {
6671
binding().upcalls()
6772
}
73+
74+
pub static GC_THREADS: OnceCell<Mutex<HashSet<ThreadId>>> = OnceCell::new();
75+
76+
pub(crate) fn register_gc_thread(thread_id: ThreadId) {
77+
let mut gc_threads = GC_THREADS.get().unwrap().lock().unwrap();
78+
gc_threads.insert(thread_id);
79+
}
80+
81+
pub(crate) fn unregister_gc_thread(thread_id: ThreadId) {
82+
let mut gc_threads = GC_THREADS.get().unwrap().lock().unwrap();
83+
gc_threads.remove(&thread_id);
84+
}
85+
86+
pub(crate) fn is_gc_thread(thread_id: ThreadId) -> bool {
87+
let gc_threads = GC_THREADS.get().unwrap().lock().unwrap();
88+
gc_threads.contains(&thread_id)
89+
}
90+
91+
fn handle_gc_thread_panic(panic_info: &PanicInfo) {
92+
eprintln!("ERROR: An MMTk GC thread panicked. This is a bug.");
93+
eprintln!("{panic_info}");
94+
95+
let bt = std::backtrace::Backtrace::capture();
96+
match bt.status() {
97+
std::backtrace::BacktraceStatus::Unsupported => {
98+
eprintln!("Backtrace is unsupported.")
99+
}
100+
std::backtrace::BacktraceStatus::Disabled => {
101+
eprintln!("Backtrace is disabled.");
102+
eprintln!("run with `RUST_BACKTRACE=1` environment variable to display a backtrace");
103+
}
104+
std::backtrace::BacktraceStatus::Captured => {
105+
eprintln!("{bt}");
106+
}
107+
s => {
108+
eprintln!("Unknown backtrace status: {s:?}");
109+
}
110+
}
111+
112+
std::process::abort();
113+
}
114+
115+
pub(crate) fn set_panic_hook() {
116+
if GC_THREADS.set(Default::default()).is_err() {
117+
return;
118+
}
119+
120+
let old_hook = std::panic::take_hook();
121+
122+
std::panic::set_hook(Box::new(move |panic_info| {
123+
if is_gc_thread(std::thread::current().id()) {
124+
handle_gc_thread_panic(panic_info);
125+
} else {
126+
old_hook(panic_info);
127+
}
128+
}));
129+
}

0 commit comments

Comments
 (0)