use rtrb::Consumer;
use std::sync::atomic::{AtomicU64, Ordering};
macro_rules! define_gc_item {
(
$(
$(#[$attr:meta])*
$variant:ident($inner:ty) = $id:literal
),+ $(,)?
) => {
#[expect(missing_docs, reason = "Docs for generated enum variants are provided by the macro invocation site — repeating would duplicate the doc contract")]
pub enum GcItem {
$(
$(#[$attr])*
$variant(Box<$inner>),
)*
}
impl GcItem {
fn type_id(&self) -> u8 {
match self {
$(
$(#[$attr])*
GcItem::$variant(_) => $id,
)*
}
}
unsafe fn from_raw_parts(ptr: *mut std::ffi::c_void, type_id: u8) -> Option<Self> {
match type_id {
$(
$(#[$attr])*
$id => Some(GcItem::$variant(unsafe {
Box::from_raw(ptr as *mut $inner)
})),
)*
_ => None,
}
}
pub(crate) fn into_packed(self) -> u64 {
let type_id = self.type_id();
let ptr = match self {
$(
$(#[$attr])*
GcItem::$variant(b) => Box::into_raw(b) as *mut std::ffi::c_void,
)*
};
debug_assert!(
(ptr as u64) < (1u64 << 56),
"GC pointer 0x{:016X} exceeds 56 bits — packing scheme is unsafe \
on this system (requires LA57 with 57-bit canonical addresses or less).",
ptr as u64
);
((type_id as u64) << 56) | (ptr as u64 & 0x00FF_FFFF_FFFF_FFFF)
}
}
};
}
define_gc_item! {
Model(crate::models::StaticModel) = 1,
Resampler(crate::dsp::resampler::NamResampler) = 2,
#[cfg(any(feature = "standalone", feature = "clap-plugin", test))]
CabSimIr(crate::dsp::cabsim::loader::CabSimIr) = 3,
#[cfg(any(feature = "standalone", feature = "clap-plugin", test))]
CabConvEngine(crate::dsp::cabsim::conv::ConvEngine) = 4,
Oversample(crate::dsp::oversample::OversampleEngine) = 5,
#[cfg(test)]
Test(std::sync::Arc<std::sync::atomic::AtomicU32>) = 255,
}
impl GcItem {
unsafe fn from_packed(packed: u64) -> Option<Self> {
if packed == 0 {
return None;
}
let type_id = ((packed >> 56) & 0xFF) as u8;
if type_id == 0 {
return None;
}
let ptr = (packed & 0x00FF_FFFF_FFFF_FFFF) as *mut std::ffi::c_void;
unsafe { Self::from_raw_parts(ptr, type_id) }
}
}
pub struct GcOverflowBuffer {
pub(crate) slots: Box<[AtomicU64]>,
write_idx: AtomicU64,
}
impl GcOverflowBuffer {
#[cold]
pub fn new(capacity: usize) -> Self {
assert!(
capacity > 0,
"GcOverflowBuffer: capacity must be greater than 0 to avoid division by zero panic."
);
let mut slots = Vec::with_capacity(capacity);
for _ in 0..capacity {
slots.push(AtomicU64::new(0));
}
Self {
slots: slots.into_boxed_slice(),
write_idx: AtomicU64::new(0),
}
}
pub fn push(&self, item: GcItem) -> bool {
let packed = item.into_packed();
let len = self.slots.len() as u64;
let idx = (self.write_idx.fetch_add(1, Ordering::Relaxed) % len) as usize;
let old = self.slots[idx].swap(packed, Ordering::AcqRel);
old != 0
}
pub fn drain(&self, rt_status: &super::RtStatusFlags) -> Vec<GcItem> {
let mut items = Vec::with_capacity(self.slots.len());
for i in 0..self.slots.len() {
let packed = self.slots[i].swap(0, Ordering::AcqRel);
if packed == 0 {
continue;
}
unsafe {
match GcItem::from_packed(packed) {
Some(item) => items.push(item),
None => {
rt_status.set_flag(super::RT_STATUS_GC_CORRUPTED);
}
}
}
}
items
}
}
impl Default for GcOverflowBuffer {
fn default() -> Self {
Self::new(64)
}
}
#[inline(always)]
pub fn gc_cascade(
mut item: Option<GcItem>,
gc_producer: &mut rtrb::Producer<GcItem>,
parking_lot: &mut [Option<GcItem>; 16],
gc_overflow: &GcOverflowBuffer,
rt_status: &super::RtStatusFlags,
) {
if let Some(i) = item.take() {
if let Err(rtrb::PushError::Full(returned)) = gc_producer.push(i) {
item = Some(returned);
} else {
return;
}
}
if let Some(i) = item.take() {
let mut i_opt = Some(i);
for slot in parking_lot.iter_mut() {
if slot.is_none() {
*slot = i_opt.take();
return;
}
}
item = i_opt;
}
if let Some(i) = item.take() {
rt_status.set_flag(super::RT_STATUS_GC_TIER3);
if gc_overflow.push(i) {
rt_status.set_flag(super::RT_STATUS_GC_OVERFLOW);
}
}
}
pub fn drain_gc_channels(
gc_consumer: &mut Consumer<GcItem>,
gc_overflow: &GcOverflowBuffer,
rt_status: &super::RtStatusFlags,
) -> usize {
let mut count = 0;
while let Ok(item) = gc_consumer.pop() {
drop(item);
count += 1;
}
for item in gc_overflow.drain(rt_status) {
drop(item);
count += 1;
}
count
}