use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Condvar, Mutex};
pub struct SafepointState {
gc_requested: AtomicBool,
gc_active: AtomicBool,
gc_done: Mutex<bool>,
gc_done_cv: Condvar,
}
impl SafepointState {
pub fn new() -> Self {
Self {
gc_requested: AtomicBool::new(false),
gc_active: AtomicBool::new(false),
gc_done: Mutex::new(false),
gc_done_cv: Condvar::new(),
}
}
pub fn request_gc(&self) {
self.gc_requested.store(true, Ordering::Release);
}
#[inline(always)]
pub fn is_gc_requested(&self) -> bool {
self.gc_requested.load(Ordering::Acquire)
}
pub fn gc_begin(&self) {
self.gc_active.store(true, Ordering::Release);
let mut done = self.gc_done.lock().unwrap();
*done = false;
}
pub fn gc_end(&self) {
self.gc_requested.store(false, Ordering::Release);
self.gc_active.store(false, Ordering::Release);
let mut done = self.gc_done.lock().unwrap();
*done = true;
self.gc_done_cv.notify_all();
}
pub fn wait_for_gc(&self) {
if !self.gc_active.load(Ordering::Acquire) {
return;
}
let mut done = self.gc_done.lock().unwrap();
while !*done {
done = self.gc_done_cv.wait(done).unwrap();
}
}
pub fn is_gc_active(&self) -> bool {
self.gc_active.load(Ordering::Acquire)
}
}
impl Default for SafepointState {
fn default() -> Self {
Self::new()
}
}
#[inline(always)]
pub fn safepoint_poll(state: &SafepointState) {
if state.is_gc_requested() {
safepoint_slow_path(state);
}
}
#[cold]
fn safepoint_slow_path(state: &SafepointState) {
state.wait_for_gc();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn jit_safepoint_poll(state_ptr: *const SafepointState) {
if state_ptr.is_null() {
return;
}
let state = unsafe { &*state_ptr };
safepoint_poll(state);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_safepoint_not_requested() {
let state = SafepointState::new();
assert!(!state.is_gc_requested());
safepoint_poll(&state);
}
#[test]
fn test_safepoint_request_and_complete() {
let state = SafepointState::new();
state.request_gc();
assert!(state.is_gc_requested());
state.gc_begin();
assert!(state.is_gc_active());
state.gc_end();
assert!(!state.is_gc_requested());
assert!(!state.is_gc_active());
safepoint_poll(&state);
}
}