cfg_if::cfg_if! {
if #[cfg(feature = "futex")] {
use std::sync::atomic::AtomicI32;
use crate::YCFutexQueue;
}
}
use crate::{YCQueue, YCQueueError, YCQueueSharedMeta};
use std::sync::atomic::{AtomicU16, AtomicU64};
#[derive(Debug)]
pub struct YCQueueOwnedMeta {
pub slot_count: AtomicU16,
pub slot_size: AtomicU16,
pub u64_meta: AtomicU64,
pub ownership: Vec<AtomicU64>,
}
impl YCQueueOwnedMeta {
pub fn new(slot_count_u16: u16, slot_size_u16: u16) -> YCQueueOwnedMeta {
let slot_count = AtomicU16::new(slot_count_u16);
let slot_size = AtomicU16::new(slot_size_u16);
let u64_meta = AtomicU64::new(0);
let mut ownership =
Vec::<AtomicU64>::with_capacity((slot_count_u16 as usize).div_ceil(u64::BITS as usize));
for _i in 0..ownership.capacity() {
ownership.push(AtomicU64::new(0));
}
YCQueueOwnedMeta {
slot_count,
slot_size,
u64_meta,
ownership,
}
}
}
impl<'a> YCQueueSharedMeta<'a> {
pub fn new(meta_ref: &'a YCQueueOwnedMeta) -> YCQueueSharedMeta<'a> {
YCQueueSharedMeta {
slot_count: &meta_ref.slot_count,
slot_size: &meta_ref.slot_size,
u64_meta: &meta_ref.u64_meta,
ownership: &meta_ref.ownership,
}
}
pub fn new_from_mut_ptr(ptr: *mut u8) -> Result<YCQueueSharedMeta<'a>, YCQueueError> {
if ptr.is_null() {
return Err(YCQueueError::InvalidArgs);
}
let slot_count_ptr = ptr as *mut AtomicU16;
let slot_size_ptr = unsafe { slot_count_ptr.add(1) };
let u64_meta_ptr = unsafe { slot_size_ptr.add(1) as *mut AtomicU64 };
let slot_count = unsafe { &*slot_count_ptr };
let slot_size = unsafe { &*slot_size_ptr };
let u64_meta = unsafe { &*u64_meta_ptr };
let slot_count_u16 = slot_count.load(std::sync::atomic::Ordering::Acquire);
let ownership_count = (slot_count_u16 as usize).div_ceil(u64::BITS as usize);
let ownership_ptr = unsafe { u64_meta_ptr.add(1) };
let ownership_slice =
unsafe { std::slice::from_raw_parts_mut(ownership_ptr, ownership_count) };
Ok(YCQueueSharedMeta {
slot_count,
slot_size,
u64_meta,
ownership: ownership_slice,
})
}
}
#[derive(Debug)]
pub struct YCQueueOwnedData {
pub meta: YCQueueOwnedMeta,
pub data: Vec<u8>,
pub raw_ptr: *mut u8,
}
impl YCQueueOwnedData {
pub fn new(slot_count_u16: u16, slot_size_u16: u16) -> YCQueueOwnedData {
let meta = YCQueueOwnedMeta::new(slot_count_u16, slot_size_u16);
let mut data = vec![0_u8; (slot_count_u16 * slot_size_u16) as usize];
let raw_ptr = data.as_mut_ptr();
YCQueueOwnedData {
meta,
data,
raw_ptr,
}
}
}
#[derive(Debug)]
pub struct YCQueueSharedData<'a> {
pub meta: YCQueueSharedMeta<'a>,
pub data: &'a mut [u8],
}
impl<'a> YCQueueSharedData<'a> {
pub fn from_owned_data(owned: &'a YCQueueOwnedData) -> YCQueueSharedData<'a> {
let meta = YCQueueSharedMeta::new(&owned.meta);
let data = owned.raw_ptr;
YCQueueSharedData {
meta,
data: unsafe { std::slice::from_raw_parts_mut(data, owned.data.len()) },
}
}
}
impl<'a> YCQueue<'a> {
pub fn from_owned_data(owned: &'a YCQueueOwnedData) -> Result<YCQueue<'a>, YCQueueError> {
let shared_view = YCQueueSharedData::from_owned_data(owned);
YCQueue::new(shared_view.meta, shared_view.data)
}
pub fn from_shared_data(shared: YCQueueSharedData<'a>) -> Result<YCQueue<'a>, YCQueueError> {
YCQueue::new(shared.meta, shared.data)
}
}
#[cfg(feature = "futex")]
#[derive(Debug)]
pub struct YCFutexQueueOwnedData {
pub data: YCQueueOwnedData,
pub count: AtomicI32,
}
#[cfg(feature = "futex")]
impl YCFutexQueueOwnedData {
pub fn new(slot_count_u16: u16, slot_size_u16: u16) -> YCFutexQueueOwnedData {
let data = YCQueueOwnedData::new(slot_count_u16, slot_size_u16);
let count = AtomicI32::new(0);
YCFutexQueueOwnedData { data, count }
}
}
#[cfg(feature = "futex")]
#[derive(Debug)]
pub struct YCFutexQueueSharedData<'a> {
pub data: YCQueueSharedData<'a>,
pub count: &'a AtomicI32,
}
#[cfg(feature = "futex")]
impl<'a> YCFutexQueueSharedData<'a> {
pub fn from_owned_data(futex_queue: &'a YCFutexQueueOwnedData) -> YCFutexQueueSharedData<'a> {
let data = YCQueueSharedData::from_owned_data(&futex_queue.data);
let count = &futex_queue.count;
YCFutexQueueSharedData { data, count }
}
}
#[cfg(feature = "futex")]
impl<'a> YCFutexQueue<'a> {
pub fn from_shared_data(
shared: YCFutexQueueSharedData<'a>,
) -> Result<YCFutexQueue<'a>, YCQueueError> {
let queue = YCQueue::new(shared.data.meta, shared.data.data)?;
Ok(YCFutexQueue {
queue,
count: shared.count,
})
}
pub fn from_owned_data(
owned: &'a YCFutexQueueOwnedData,
) -> Result<YCFutexQueue<'a>, YCQueueError> {
let queue = YCQueue::from_owned_data(&owned.data)?;
Ok(YCFutexQueue {
queue,
count: &owned.count,
})
}
}
#[cfg(test)]
mod queue_alloc_helpers_tests {
use super::*;
use std::sync::atomic::Ordering;
#[test]
fn test_shared_meta() {
let slot_count: u16 = 128;
let slot_size: u16 = 64;
let owned_meta = YCQueueOwnedMeta::new(slot_count, slot_size);
let shared_meta: YCQueueSharedMeta<'_> = YCQueueSharedMeta::new(&owned_meta);
assert_eq!(
owned_meta.u64_meta.load(Ordering::Acquire),
shared_meta.u64_meta.load(Ordering::Acquire)
);
let num_atomics = owned_meta.ownership.len();
for i in 0..num_atomics {
assert_eq!(
owned_meta.ownership[i].load(Ordering::Acquire),
shared_meta.ownership[i].load(Ordering::Acquire)
);
}
let new_value: u64 = 12345;
owned_meta.u64_meta.store(new_value, Ordering::Release);
assert_eq!(shared_meta.u64_meta.load(Ordering::Acquire), new_value);
for i in 0..num_atomics {
owned_meta.ownership[i].store(i as u64, Ordering::Release);
}
for i in 0..num_atomics {
assert_eq!(i as u64, shared_meta.ownership[i].load(Ordering::Acquire));
}
let new_new_value = 54321;
shared_meta.u64_meta.store(new_new_value, Ordering::Release);
assert_eq!(owned_meta.u64_meta.load(Ordering::Acquire), new_new_value);
for i in 0..num_atomics {
shared_meta.ownership[i].store(new_new_value + (i as u64), Ordering::Release);
}
for i in 0..num_atomics {
assert_eq!(
new_new_value + (i as u64),
owned_meta.ownership[i].load(Ordering::Acquire)
);
}
}
#[test]
fn test_shared_data() {
let slot_count: u16 = 4;
let slot_size: u16 = 8;
let mut owned_data = YCQueueOwnedData::new(slot_count, slot_size);
for (idx, byte) in owned_data.data.iter_mut().enumerate() {
*byte = idx as u8;
}
let data_len = owned_data.data.len();
{
let shared = YCQueueSharedData::from_owned_data(&owned_data);
let data = shared.data;
assert_eq!(data, owned_data.data.as_slice());
data[0] = 0xAA;
data[data_len - 1] = 0xBB;
}
assert_eq!(owned_data.data[0], 0xAA);
assert_eq!(owned_data.data[data_len - 1], 0xBB);
owned_data.data[1] = 0xCC;
let mid = data_len / 2;
owned_data.data[mid] = 0xDD;
let shared = YCQueueSharedData::from_owned_data(&owned_data);
let data = shared.data;
assert_eq!(data[1], 0xCC);
assert_eq!(data[mid], 0xDD);
}
#[cfg(feature = "futex")]
#[test]
fn test_futex_shared_queue_and_counter() {
let slot_count: u16 = 4;
let slot_size: u16 = 16;
let mut owned = YCFutexQueueOwnedData::new(slot_count, slot_size);
for (idx, byte) in owned.data.data.iter_mut().enumerate() {
*byte = idx as u8;
}
let data_len = owned.data.data.len();
{
let shared = YCFutexQueueSharedData::from_owned_data(&owned);
let data = shared.data.data;
assert_eq!(data, owned.data.data.as_slice());
data[0] = 0xAA;
data[data.len() - 1] = 0xBB;
}
assert_eq!(owned.data.data[0], 0xAA);
assert_eq!(owned.data.data[data_len - 1], 0xBB);
owned.data.data[1] = 0xCC;
owned.data.data[data_len / 2] = 0xDD;
{
let shared_again = YCFutexQueueSharedData::from_owned_data(&owned);
let data_again = shared_again.data.data;
assert_eq!(data_again[1], 0xCC);
assert_eq!(data_again[data_again.len() / 2], 0xDD);
}
owned.count.store(123, Ordering::Release);
{
let shared = YCFutexQueueSharedData::from_owned_data(&owned);
assert_eq!(shared.count.load(Ordering::Acquire), 123);
shared.count.store(77, Ordering::Release);
}
assert_eq!(owned.count.load(Ordering::Acquire), 77);
}
}