Created
January 10, 2021 00:22
-
-
Save benaubin/1d0696f7d53e9f3d5205dd5bbd454ca5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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