Skip to content

Instantly share code, notes, and snippets.

@benaubin
Created January 10, 2021 00:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benaubin/1d0696f7d53e9f3d5205dd5bbd454ca5 to your computer and use it in GitHub Desktop.
Save benaubin/1d0696f7d53e9f3d5205dd5bbd454ca5 to your computer and use it in GitHub Desktop.
use core::ptr::{self, NonNull};
use std::sync::atomic::{self, AtomicBool, AtomicPtr};
struct Shared<T: Send> {
hung_up: AtomicBool,
slot: AtomicPtr<T>,
}
pub struct Writer<T: Send>(NonNull<Shared<T>>);
pub struct Reader<T: Send>(NonNull<Shared<T>>);
// Creates a new mailslot
pub fn new<T: Send>() -> (Writer<T>, Reader<T>) {
let shared = Box::leak(Box::new(Shared {
hung_up: AtomicBool::from(false),
slot: AtomicPtr::default(),
}));
let shared = unsafe { NonNull::new_unchecked(shared) };
(Writer(shared), Reader(shared))
}
impl<T: Send> Writer<T> {
fn shared<'a>(&'a self) -> &'a Shared<T> {
// safe because we ensure that the inner is not dropped until both halves are dropped
unsafe { self.0.as_ref() }
}
/// Put mail into the slot. Err() if the slot is full, returning the value that was attempted to be put in
pub fn put(&mut self, val: Box<T>) -> Result<(), Box<T>> {
let ptr = Box::leak(val);
match &self.shared().slot.compare_exchange(
ptr::null_mut(),
ptr,
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
) {
Ok(_) => Ok(()),
Err(_) => {
// failed to share pointer, so it's safe to recreate the box here
Err(unsafe { Box::from_raw(ptr) })
}
}
}
}
impl<T: Send> Reader<T> {
fn shared<'a>(&'a self) -> &'a Shared<T> {
// safe because we ensure that the inner is not dropped until both halves are dropped
unsafe { self.0.as_ref() }
}
/// Take mail from the slot
pub fn take(&mut self) -> Option<Box<T>> {
let ptr = self
.shared()
.slot
.swap(ptr::null_mut(), atomic::Ordering::AcqRel);
NonNull::new(ptr).map(|ptr| unsafe { Box::from_raw(ptr.as_ptr()) })
}
}
impl<T: Send> Drop for Writer<T> {
fn drop(&mut self) {
if self.shared().hung_up.swap(true, atomic::Ordering::Relaxed) == true {
// safe because other half has already hung up - we have exclusive access
unsafe {
Box::from_raw(self.0.as_ptr());
}
}
}
}
impl<T: Send> Drop for Reader<T> {
fn drop(&mut self) {
if self.shared().hung_up.swap(true, atomic::Ordering::Relaxed) == true {
// safe because other half has already hung up - we have exclusive access
unsafe {
Box::from_raw(self.0.as_ptr());
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment