diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 2466428c..8a991d65 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -1,6 +1,5 @@ use core::cell::RefCell; use core::marker::PhantomData; -use core::pin::Pin; use alloc::boxed::Box; use alloc::vec::Vec; @@ -107,7 +106,7 @@ pub struct Mixer<'gba> { // SAFETY: Has to go before buffer because it holds a reference to it _interrupt_handler: InterruptHandler, - buffer: Pin>, + buffer: raw_box::RawBoxDrop, channels: [Option; 8], indices: [i32; 8], frequency: Frequency, @@ -144,7 +143,8 @@ pub struct ChannelId(usize, i32); impl Mixer<'_> { pub(super) fn new(frequency: Frequency) -> Self { - let buffer = Box::pin_in(MixerBuffer::new(frequency), InternalAllocator); + let buffer = + raw_box::RawBoxDrop::new(Box::new_in(MixerBuffer::new(frequency), InternalAllocator)); // SAFETY: you can only ever have 1 Mixer at a time let fifo_timer = unsafe { Timer::new(0) }; @@ -157,15 +157,21 @@ impl Mixer<'_> { .set_interrupt(true) .set_overflow_amount(frequency.buffer_size() as u16); - let buffer_pointer_for_interrupt_handler: &MixerBuffer = &buffer; + struct SendPtr(*const T); + unsafe impl Send for SendPtr {} + unsafe impl Sync for SendPtr {} - // SAFETY: dropping the lifetime, sound because interrupt handler dropped before the buffer is - // In the case of the mixer being forgotten, both stay alive so okay - let buffer_pointer_for_interrupt_handler: &MixerBuffer = - unsafe { core::mem::transmute(buffer_pointer_for_interrupt_handler) }; + let ptr_for_interrupt_handler = SendPtr(&*buffer); + + // SAFETY: the interrupt handler will be dropped before the buffer is so this never accesses + // freed memory. Also the dereference happens in a critical section to ensure that + // we don't end up accessing while dropping let interrupt_handler = unsafe { - add_interrupt_handler(interrupt_timer.interrupt(), |cs| { - buffer_pointer_for_interrupt_handler.swap(cs); + add_interrupt_handler(interrupt_timer.interrupt(), move |cs| { + // needed to ensure that rust doesn't only capture the field + let _ = &ptr_for_interrupt_handler; + + (*ptr_for_interrupt_handler.0).swap(cs); }) }; @@ -548,6 +554,35 @@ impl MixerBuffer { } } +mod raw_box { + use core::ops::Deref; + + use alloc::boxed::Box; + + pub struct RawBoxDrop(*mut T, A); + + impl RawBoxDrop { + pub fn new(inner: Box) -> Self { + let (ptr, allocator) = Box::into_raw_with_allocator(inner); + Self(ptr, allocator) + } + } + + impl Deref for RawBoxDrop { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } + } + + impl Drop for RawBoxDrop { + fn drop(&mut self) { + unsafe { Box::from_raw_in(self.0, self.1.clone()) }; + } + } +} + #[cfg(test)] mod test { use crate::fixnum::num;