#[cfg(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
))]
use contracts::*;
#[cfg(not(any(
all(test, feature = "check_contracts_in_tests"),
feature = "check_contracts"
)))]
use disabled_contracts::*;
use std::mem::MaybeUninit;
use std::ptr::NonNull;
use std::sync::atomic::AtomicPtr;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use crate::magazine::Magazine;
use crate::magazine_impl::MagazineImpl;
use crate::magazine_impl::MagazineStorage;
#[repr(C)]
#[repr(align(16))]
pub struct MagazineStack {
top_of_stack: AtomicPtr<MagazineStorage>,
generation: AtomicUsize,
}
extern "C" {
fn slitter__stack_push(stack: &MagazineStack, mag: NonNull<MagazineStorage>);
fn slitter__stack_pop(stack: &MagazineStack, out_mag: *mut NonNull<MagazineStorage>) -> bool;
fn slitter__stack_try_pop(
stack: &MagazineStack,
out_mag: *mut NonNull<MagazineStorage>,
) -> bool;
}
impl MagazineStack {
pub fn new() -> Self {
Self {
top_of_stack: Default::default(),
generation: AtomicUsize::new(0),
}
}
#[requires(mag.check_rep(None).is_ok(),
"Magazine must make sense.")]
#[inline(always)]
pub fn push<const PUSH_MAG: bool>(&self, mag: Magazine<PUSH_MAG>) {
if let Some(storage) = mag.0.storage() {
unsafe { slitter__stack_push(&self, storage.into()) }
}
}
#[ensures(ret.is_some() ->
ret.as_ref().unwrap().check_rep(None).is_ok(),
"Magazine should make sense.")]
#[inline(always)]
pub fn pop<const PUSH_MAG: bool>(&self) -> Option<Magazine<PUSH_MAG>> {
if self.top_of_stack.load(Ordering::Relaxed).is_null() {
return None;
}
let mut dst: MaybeUninit<NonNull<MagazineStorage>> = MaybeUninit::uninit();
if unsafe { slitter__stack_pop(&self, dst.as_mut_ptr()) } {
let storage = unsafe { &mut *dst.assume_init().as_ptr() };
Some(Magazine(MagazineImpl::new(Some(storage))))
} else {
None
}
}
#[ensures(ret.is_some() ->
ret.as_ref().unwrap().check_rep(None).is_ok(),
"Magazine should make sense.")]
#[inline(always)]
pub fn try_pop<const PUSH_MAG: bool>(&self) -> Option<Magazine<PUSH_MAG>> {
if self.top_of_stack.load(Ordering::Relaxed).is_null() {
return None;
}
let mut dst: MaybeUninit<NonNull<MagazineStorage>> = MaybeUninit::uninit();
if unsafe { slitter__stack_try_pop(&self, dst.as_mut_ptr()) } {
let storage = unsafe { &mut *dst.assume_init().as_ptr() };
Some(Magazine(MagazineImpl::new(Some(storage))))
} else {
None
}
}
}
unsafe impl Send for MagazineStack {}
unsafe impl Sync for MagazineStack {}
#[test]
fn magazine_stack_smoke_test() {
let rack = crate::rack::get_default_rack();
let stack = MagazineStack::new();
stack.push(rack.allocate_empty_magazine::<false>());
stack.push(rack.allocate_empty_magazine::<true>());
assert!(stack.pop::<false>().is_some());
stack.push(rack.allocate_empty_magazine::<true>());
assert!(stack.pop::<true>().is_some());
assert!(stack.pop::<false>().is_some());
assert!(stack.pop::<true>().is_none());
}