Skip to content

Instantly share code, notes, and snippets.

@l-Luna
Last active July 2, 2025 15:08
Show Gist options
  • Select an option

  • Save l-Luna/c4da53bdd681b9ff0d29f43fdb17e7a4 to your computer and use it in GitHub Desktop.

Select an option

Save l-Luna/c4da53bdd681b9ff0d29f43fdb17e7a4 to your computer and use it in GitHub Desktop.
use std::cell::UnsafeCell;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
struct DoubleBufInner<T: Clone + 'static> {
buf_a: UnsafeCell<T>,
buf_b: UnsafeCell<T>,
a_externally_visible: AtomicBool,
a_internally_visible: AtomicBool,
// tracks whether the visible buffer is currently being cloned by the non-writer thread,
// so that swapping (and subsequent tearing) can be skipped
busy: AtomicBool,
}
impl<T: Clone + 'static> DoubleBufInner<T> {
fn new(initial: T) -> Self {
Self {
buf_a: UnsafeCell::new(initial.clone()),
buf_b: UnsafeCell::new(initial),
a_externally_visible: AtomicBool::new(true),
a_internally_visible: AtomicBool::new(true),
busy: AtomicBool::new(false),
}
}
// `mark_busy` must only be set from the designated read-only thread
unsafe fn read(&self, external: bool) -> T {
// mark this buffer as being used
if external {
self.busy.store(true, Ordering::Release);
}
// fetch the buffer to read from
let flag = if external {
&self.a_externally_visible
} else {
&self.a_internally_visible
};
let buf = if flag.load(Ordering::Acquire) {
&self.buf_a
} else {
&self.buf_b
};
// and read + clone, which may take time
let value = unsafe { buf.get().as_ref() }
.unwrap()
.clone();
// unmark this buffer as being used
if external {
self.busy.store(false, Ordering::Release);
}
value
}
// must only be called by the designated write thread
unsafe fn write_swap(&self, value: T) {
// fetch the buffer to write to, which is whichever buffer is unavailable for reading from
// externally
let from = self.a_externally_visible.load(Ordering::Acquire);
let buf = if from { &self.buf_b } else { &self.buf_a };
// we have exclusive access, so write to it as a mutable reference
unsafe {
*buf.get().as_mut().unwrap() = value;
}
// if the externally visible buffer is busy, make the written-to buffer the internally
// visible one and leave the busy buffer externally visible
if self.busy.load(Ordering::Acquire) {
self.a_internally_visible.store(!from, Ordering::Release);
} else {
// otherwise, make the written-to buffer visible to both
self.a_internally_visible.store(!from, Ordering::Release);
self.a_externally_visible.store(!from, Ordering::Release);
}
}
}
pub struct SharedReader<T: Clone + 'static> {
inner: Arc<DoubleBufInner<T>>,
_unsync: PhantomData<UnsafeCell<()>>,
}
impl<T: Clone + 'static> SharedReader<T> {
pub fn read(&self) -> T {
unsafe { self.inner.read(true) }
}
}
impl<T: Clone + 'static + Debug> Debug for SharedReader<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "SharedReader {{ ")?;
self.read().fmt(f)?;
write!(f, " }}")
}
}
pub struct SharedWriter<T: Clone + 'static> {
inner: Arc<DoubleBufInner<T>>,
_unsync: PhantomData<UnsafeCell<()>>,
}
impl<T: Clone + 'static> SharedWriter<T> {
pub fn read(&self) -> T {
unsafe { self.inner.read(false) }
}
pub fn write(&self, value: T) {
unsafe {
self.inner.write_swap(value);
}
}
}
impl<T: Clone + 'static + Debug> Debug for SharedWriter<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "SharedWriter {{ ")?;
self.read().fmt(f)?;
write!(f, " }}")
}
}
pub fn create_buf<T: Clone + 'static>(initial: T) -> (SharedReader<T>, SharedWriter<T>) {
let inner = Arc::new(DoubleBufInner::new(initial));
(
SharedReader {
inner: inner.clone(),
_unsync: PhantomData,
},
SharedWriter {
inner,
_unsync: PhantomData,
},
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment