use core::fmt;
use core::sync::atomic::{Ordering, compiler_fence};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NativePlatform {
Linux,
MacOs,
Windows,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NativeArchitecture {
Arm64,
Amd64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AuthorityMechanism {
DescriptorSeals,
MaximumPortRights,
ExactHandleRights,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct NativeMemoryCapabilities {
platform: NativePlatform,
architecture: NativeArchitecture,
authority: AuthorityMechanism,
}
impl NativeMemoryCapabilities {
pub const fn platform(self) -> NativePlatform {
self.platform
}
pub const fn architecture(self) -> NativeArchitecture {
self.architecture
}
pub const fn authority_mechanism(self) -> AuthorityMechanism {
self.authority
}
pub const fn supports_replacement_growth(self) -> bool {
true
}
pub const fn supports_in_place_growth(self) -> bool {
false
}
pub const fn supports_post_share_permission_changes(self) -> bool {
false
}
pub const fn releases_on_drop(self) -> bool {
true
}
}
pub const fn native_memory_capabilities() -> NativeMemoryCapabilities {
NativeMemoryCapabilities {
platform: native_platform(),
architecture: native_architecture(),
authority: authority_mechanism(),
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WriterOwner {
Creator,
Peer,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MemoryAccess {
ReadOnly,
ReadWrite,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PermissionPlan {
writer: WriterOwner,
}
impl PermissionPlan {
pub const fn new(writer: WriterOwner) -> Self {
Self { writer }
}
pub const fn writer(self) -> WriterOwner {
self.writer
}
pub const fn creator_access(self) -> MemoryAccess {
match self.writer {
WriterOwner::Creator => MemoryAccess::ReadWrite,
WriterOwner::Peer => MemoryAccess::ReadOnly,
}
}
pub const fn peer_access(self) -> MemoryAccess {
match self.writer {
WriterOwner::Creator => MemoryAccess::ReadOnly,
WriterOwner::Peer => MemoryAccess::ReadWrite,
}
}
pub const fn library_view_executable(self) -> bool {
false
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GrowthPolicy {
Fixed,
ReplaceBeforeShare {
maximum_len: usize,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CleanupPolicy {
ReleaseOnDrop,
ClearThenRelease,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SealPolicy {
RequiredOnShare,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RegionOptions {
logical_len: usize,
growth: GrowthPolicy,
cleanup: CleanupPolicy,
permissions: PermissionPlan,
seal: SealPolicy,
}
impl RegionOptions {
pub const fn fixed(logical_len: usize, writer: WriterOwner) -> Self {
Self {
logical_len,
growth: GrowthPolicy::Fixed,
cleanup: CleanupPolicy::ClearThenRelease,
permissions: PermissionPlan::new(writer),
seal: SealPolicy::RequiredOnShare,
}
}
pub const fn growable(logical_len: usize, maximum_len: usize, writer: WriterOwner) -> Self {
Self {
logical_len,
growth: GrowthPolicy::ReplaceBeforeShare { maximum_len },
cleanup: CleanupPolicy::ClearThenRelease,
permissions: PermissionPlan::new(writer),
seal: SealPolicy::RequiredOnShare,
}
}
pub const fn with_cleanup(mut self, cleanup: CleanupPolicy) -> Self {
self.cleanup = cleanup;
self
}
pub const fn logical_len(self) -> usize {
self.logical_len
}
pub const fn growth(self) -> GrowthPolicy {
self.growth
}
pub const fn cleanup(self) -> CleanupPolicy {
self.cleanup
}
pub const fn permissions(self) -> PermissionPlan {
self.permissions
}
pub const fn seal(self) -> SealPolicy {
self.seal
}
pub const fn maximum_len(self) -> usize {
match self.growth {
GrowthPolicy::Fixed => self.logical_len,
GrowthPolicy::ReplaceBeforeShare { maximum_len } => maximum_len,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RegionState {
Quiescent,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RegionStatus {
pub state: RegionState,
pub logical_len: usize,
pub mapped_len: usize,
pub maximum_len: usize,
pub can_grow: bool,
pub permissions: PermissionPlan,
pub cleanup: CleanupPolicy,
pub seal: SealPolicy,
}
#[derive(Debug)]
pub enum MemoryError {
ZeroLength,
MaximumBelowInitial {
initial: usize,
maximum: usize,
},
FixedSize,
ShrinkUnsupported {
current: usize,
requested: usize,
},
MaximumExceeded {
requested: usize,
maximum: usize,
},
Platform {
operation: &'static str,
},
RandomnessUnavailable,
}
impl fmt::Display for MemoryError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "native shared-memory operation failed: {self:?}")
}
}
impl std::error::Error for MemoryError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
#[cfg(target_os = "linux")]
impl From<crate::backend::linux::LinuxError> for MemoryError {
fn from(_: crate::backend::linux::LinuxError) -> Self {
Self::Platform {
operation: "allocate",
}
}
}
#[cfg(target_os = "macos")]
impl From<crate::backend::macos::MachError> for MemoryError {
fn from(_: crate::backend::macos::MachError) -> Self {
Self::Platform {
operation: "allocate",
}
}
}
#[cfg(target_os = "windows")]
impl From<crate::backend::windows::WindowsError> for MemoryError {
fn from(_: crate::backend::windows::WindowsError) -> Self {
Self::Platform {
operation: "allocate",
}
}
}
pub struct NativeRegion {
inner: Option<PlatformQuiescentRegion>,
logical_len: usize,
options: RegionOptions,
}
impl NativeRegion {
pub fn allocate(options: RegionOptions) -> Result<Self, MemoryError> {
validate_options(options)?;
let inner = PlatformQuiescentRegion::new(options.logical_len())?;
Ok(Self {
inner: Some(inner),
logical_len: options.logical_len(),
options,
})
}
pub const fn options(&self) -> RegionOptions {
self.options
}
pub fn status(&self) -> RegionStatus {
let maximum_len = self.options.maximum_len();
RegionStatus {
state: RegionState::Quiescent,
logical_len: self.logical_len,
mapped_len: self.inner().len(),
maximum_len,
can_grow: matches!(
self.options.growth(),
GrowthPolicy::ReplaceBeforeShare { .. }
) && self.logical_len < maximum_len,
permissions: self.options.permissions(),
cleanup: self.options.cleanup(),
seal: self.options.seal(),
}
}
pub fn initialize<R>(&mut self, operation: impl FnOnce(&mut [u8]) -> R) -> R {
let logical_len = self.logical_len;
operation(&mut self.inner_mut().as_bytes_mut()[..logical_len])
}
pub fn clear(&mut self) {
clear_mapping(self.inner_mut());
}
pub fn grow(&mut self, requested: usize) -> Result<(), MemoryError> {
if requested == 0 {
return Err(MemoryError::ZeroLength);
}
if requested < self.logical_len {
return Err(MemoryError::ShrinkUnsupported {
current: self.logical_len,
requested,
});
}
if requested == self.logical_len {
return Ok(());
}
let maximum = match self.options.growth() {
GrowthPolicy::Fixed => return Err(MemoryError::FixedSize),
GrowthPolicy::ReplaceBeforeShare { maximum_len } => maximum_len,
};
if requested > maximum {
return Err(MemoryError::MaximumExceeded { requested, maximum });
}
let mut replacement = PlatformQuiescentRegion::new(requested)?;
replacement.as_bytes_mut()[..self.logical_len]
.copy_from_slice(&self.inner().as_bytes()[..self.logical_len]);
let mut previous = self
.inner
.replace(replacement)
.expect("managed region always owns its mapping");
if self.options.cleanup() == CleanupPolicy::ClearThenRelease {
clear_mapping(&mut previous);
}
self.logical_len = requested;
Ok(())
}
pub fn close(mut self) {
self.release();
}
pub fn destroy(mut self) {
if let Some(inner) = self.inner.as_mut() {
clear_mapping(inner);
}
drop(self.inner.take());
}
pub fn prepare_for_sharing(mut self) -> Result<NativeShareRequest, MemoryError> {
let incarnation =
crate::backend::mint_incarnation().map_err(|()| MemoryError::RandomnessUnavailable)?;
Ok(NativeShareRequest {
inner: self.inner.take(),
incarnation,
logical_len: self.logical_len,
permissions: self.options.permissions(),
seal: self.options.seal(),
cleanup: self.options.cleanup(),
})
}
pub(crate) fn prepare_with_writer(
mut self,
writer: WriterOwner,
) -> Result<NativeShareRequest, MemoryError> {
self.options.permissions = PermissionPlan::new(writer);
self.prepare_for_sharing()
}
fn inner(&self) -> &PlatformQuiescentRegion {
self.inner
.as_ref()
.expect("managed region always owns its mapping")
}
fn inner_mut(&mut self) -> &mut PlatformQuiescentRegion {
self.inner
.as_mut()
.expect("managed region always owns its mapping")
}
fn release(&mut self) {
if let Some(inner) = self.inner.as_mut()
&& self.options.cleanup() == CleanupPolicy::ClearThenRelease
{
clear_mapping(inner);
}
drop(self.inner.take());
}
}
pub struct NativeShareRequest {
inner: Option<PlatformQuiescentRegion>,
#[allow(dead_code)]
incarnation: [u8; 16],
logical_len: usize,
permissions: PermissionPlan,
seal: SealPolicy,
cleanup: CleanupPolicy,
}
impl NativeShareRequest {
pub const fn permissions(&self) -> PermissionPlan {
self.permissions
}
pub const fn seal_policy(&self) -> SealPolicy {
self.seal
}
pub fn mapped_len(&self) -> usize {
self.inner
.as_ref()
.expect("share request always owns its mapping")
.len()
}
#[allow(dead_code)]
pub(crate) const fn logical_len(&self) -> usize {
self.logical_len
}
#[allow(dead_code)]
pub(crate) const fn incarnation(&self) -> [u8; 16] {
self.incarnation
}
#[allow(dead_code)]
pub(crate) fn native_spec(&self, region_id: u128) -> Option<crate::protocol::NativeRegionSpec> {
crate::protocol::NativeRegionSpec::new(
region_id,
self.incarnation,
self.permissions.writer() as u32,
self.logical_len,
self.mapped_len(),
)
}
#[cfg(target_os = "linux")]
pub(crate) fn into_linux_quiescent(
mut self,
) -> (crate::backend::linux::QuiescentRegion, CleanupPolicy) {
let inner = self
.inner
.take()
.expect("share request always owns its mapping");
(inner, self.cleanup)
}
#[cfg(target_os = "macos")]
pub(crate) fn into_macos_quiescent(
mut self,
) -> (crate::backend::macos::QuiescentRegion, CleanupPolicy) {
let inner = self
.inner
.take()
.expect("share request always owns its mapping");
(inner, self.cleanup)
}
#[cfg(target_os = "windows")]
pub(crate) fn into_windows_quiescent(
mut self,
) -> (crate::backend::windows::QuiescentRegion, CleanupPolicy) {
let inner = self
.inner
.take()
.expect("share request always owns its mapping");
(inner, self.cleanup)
}
pub fn destroy(mut self) {
if let Some(inner) = self.inner.as_mut() {
clear_mapping(inner);
}
drop(self.inner.take());
}
fn release(&mut self) {
if let Some(inner) = self.inner.as_mut()
&& self.cleanup == CleanupPolicy::ClearThenRelease
{
clear_mapping(inner);
}
drop(self.inner.take());
}
}
impl Drop for NativeShareRequest {
fn drop(&mut self) {
self.release();
}
}
fn clear_mapping(region: &mut PlatformQuiescentRegion) {
for byte in region.as_bytes_mut() {
unsafe { core::ptr::write_volatile(byte, 0) };
}
compiler_fence(Ordering::SeqCst);
}
impl Drop for NativeRegion {
fn drop(&mut self) {
self.release();
}
}
fn validate_options(options: RegionOptions) -> Result<(), MemoryError> {
if options.logical_len() == 0 {
return Err(MemoryError::ZeroLength);
}
let maximum = options.maximum_len();
if maximum < options.logical_len() {
return Err(MemoryError::MaximumBelowInitial {
initial: options.logical_len(),
maximum,
});
}
Ok(())
}
#[cfg(target_os = "linux")]
pub(crate) type PlatformQuiescentRegion = crate::backend::linux::QuiescentRegion;
#[cfg(target_os = "macos")]
pub(crate) type PlatformQuiescentRegion = crate::backend::macos::QuiescentRegion;
#[cfg(target_os = "windows")]
pub(crate) type PlatformQuiescentRegion = crate::backend::windows::QuiescentRegion;
#[cfg(target_os = "linux")]
const fn native_platform() -> NativePlatform {
NativePlatform::Linux
}
#[cfg(target_os = "macos")]
const fn native_platform() -> NativePlatform {
NativePlatform::MacOs
}
#[cfg(target_os = "windows")]
const fn native_platform() -> NativePlatform {
NativePlatform::Windows
}
#[cfg(target_arch = "aarch64")]
const fn native_architecture() -> NativeArchitecture {
NativeArchitecture::Arm64
}
#[cfg(target_arch = "x86_64")]
const fn native_architecture() -> NativeArchitecture {
NativeArchitecture::Amd64
}
#[cfg(target_os = "linux")]
const fn authority_mechanism() -> AuthorityMechanism {
AuthorityMechanism::DescriptorSeals
}
#[cfg(target_os = "macos")]
const fn authority_mechanism() -> AuthorityMechanism {
AuthorityMechanism::MaximumPortRights
}
#[cfg(target_os = "windows")]
const fn authority_mechanism() -> AuthorityMechanism {
AuthorityMechanism::ExactHandleRights
}
#[cfg(test)]
#[path = "memory_test.rs"]
mod tests;