use core::cell::Cell;
use core::fmt;
use core::marker::PhantomData;
use crate::memory;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RegionId(u128);
impl RegionId {
pub const fn new(value: u128) -> Option<Self> {
if value == 0 { None } else { Some(Self(value)) }
}
pub const fn get(self) -> u128 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WriterEndpoint {
Coordinator,
Receiver,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RegionSpec {
pub id: RegionId,
pub writer: WriterEndpoint,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GuardPolicy {
BestEffort,
Require,
Disable,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GuardCapability {
pub requested: GuardPolicy,
pub installed: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RegionOptions {
logical_len: usize,
maximum_len: usize,
guard: GuardPolicy,
}
impl RegionOptions {
pub const fn fixed(logical_len: usize) -> Self {
Self {
logical_len,
maximum_len: logical_len,
guard: GuardPolicy::BestEffort,
}
}
pub const fn with_max_bytes(mut self, maximum_len: usize) -> Self {
self.maximum_len = maximum_len;
self
}
pub const fn with_guard_policy(mut self, guard: GuardPolicy) -> Self {
self.guard = guard;
self
}
}
#[derive(Debug)]
pub enum RegionError {
Memory(memory::MemoryError),
GuardUnavailable,
}
impl fmt::Display for RegionError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Memory(error) => error.fmt(formatter),
Self::GuardUnavailable => formatter.write_str("required guard bands are unavailable"),
}
}
}
impl std::error::Error for RegionError {}
impl From<memory::MemoryError> for RegionError {
fn from(value: memory::MemoryError) -> Self {
Self::Memory(value)
}
}
pub struct PrivateRegion {
inner: memory::NativeRegion,
guard: GuardPolicy,
_not_sync: PhantomData<Cell<()>>,
}
unsafe impl Send for PrivateRegion {}
impl PrivateRegion {
pub fn allocate(options: RegionOptions) -> Result<Self, RegionError> {
let native = if options.maximum_len == options.logical_len {
memory::RegionOptions::fixed(options.logical_len, memory::WriterOwner::Creator)
} else {
memory::RegionOptions::growable(
options.logical_len,
options.maximum_len,
memory::WriterOwner::Creator,
)
};
Ok(Self {
inner: memory::NativeRegion::allocate(native)?,
guard: options.guard,
_not_sync: PhantomData,
})
}
pub fn initialize<R>(&mut self, operation: impl FnOnce(&mut [u8]) -> R) -> R {
self.inner.initialize(operation)
}
pub fn prepare(self, spec: RegionSpec) -> Result<PreparedRegion, RegionError> {
let writer = match spec.writer {
WriterEndpoint::Coordinator => memory::WriterOwner::Creator,
WriterEndpoint::Receiver => memory::WriterOwner::Peer,
};
let request = self.inner.prepare_with_writer(writer)?;
Ok(PreparedRegion {
request,
spec,
guard: GuardCapability {
requested: self.guard,
installed: false,
},
#[cfg(test)]
drop_observer: PreparedDropObserver(None),
_not_sync: PhantomData,
})
}
}
pub struct PreparedRegion {
#[cfg(test)]
drop_observer: PreparedDropObserver,
#[allow(dead_code)]
pub(crate) request: memory::NativeShareRequest,
#[allow(dead_code)]
pub(crate) spec: RegionSpec,
#[allow(dead_code)]
pub(crate) guard: GuardCapability,
_not_sync: PhantomData<Cell<()>>,
}
#[cfg(test)]
struct PreparedDropObserver(Option<std::sync::Arc<std::sync::Mutex<Vec<&'static str>>>>);
#[cfg(test)]
impl Drop for PreparedDropObserver {
fn drop(&mut self) {
if let Some(events) = &self.0 {
events.lock().unwrap().push("prepared-drop");
}
}
}
unsafe impl Send for PreparedRegion {}
impl PreparedRegion {
pub const fn guard_capability(&self) -> GuardCapability {
self.guard
}
pub(crate) const fn spec(&self) -> RegionSpec {
self.spec
}
#[allow(dead_code)]
pub(crate) fn logical_len(&self) -> usize {
self.request.logical_len()
}
#[allow(dead_code)]
pub(crate) fn mapped_len(&self) -> usize {
self.request.mapped_len()
}
#[cfg(test)]
pub(crate) fn observe_drop(
mut self,
events: std::sync::Arc<std::sync::Mutex<Vec<&'static str>>>,
) -> Self {
self.drop_observer.0 = Some(events);
self
}
#[cfg(target_os = "linux")]
pub(crate) fn into_linux_transfer_parts(
self,
) -> (memory::NativeShareRequest, RegionSpec, GuardCapability) {
let Self {
#[cfg(test)]
drop_observer: _,
request,
spec,
guard,
_not_sync: _,
} = self;
(request, spec, guard)
}
#[cfg(target_os = "macos")]
pub(crate) fn into_macos_transfer_parts(
self,
) -> (memory::NativeShareRequest, RegionSpec, GuardCapability) {
let Self {
#[cfg(test)]
drop_observer: _,
request,
spec,
guard,
_not_sync: _,
} = self;
(request, spec, guard)
}
#[cfg(target_os = "windows")]
pub(crate) fn into_windows_transfer_parts(
self,
) -> (memory::NativeShareRequest, RegionSpec, GuardCapability) {
let Self {
#[cfg(test)]
drop_observer: _,
request,
spec,
guard,
_not_sync: _,
} = self;
(request, spec, guard)
}
}
#[cfg(test)]
#[path = "region_test.rs"]
mod tests;