use crate::runtime::memory_management::MemoryHandle;
use alloc::sync::Arc;
use spin::Mutex;
#[derive(Debug)]
pub struct ManagedMemoryHandle {
descriptor: Arc<ManagedMemoryDescriptor>,
handle_count: Arc<()>,
}
#[derive(Debug)]
pub struct ManagedMemoryBinding {
descriptor: Arc<ManagedMemoryDescriptor>,
}
impl Clone for ManagedMemoryHandle {
fn clone(&self) -> Self {
Self {
descriptor: self.descriptor.clone(),
handle_count: self.handle_count.clone(),
}
}
}
pub(crate) struct ManagedMemoryDescriptor {
pub(crate) id: ManagedMemoryId,
location: Mutex<MemoryLocation>,
}
impl core::fmt::Debug for ManagedMemoryDescriptor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ManagedMemoryDescriptor")
.field("id", &self.id)
.field("location", &self.location())
.finish()
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct ManagedMemoryId {
pub(crate) value: usize,
}
impl PartialEq for ManagedMemoryDescriptor {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for ManagedMemoryDescriptor {}
#[derive(Clone, Copy, Debug)]
pub(crate) struct MemoryLocation {
pub pool: u8,
pub page: u16,
pub slice: u32,
pub init: u8,
}
impl ManagedMemoryDescriptor {
pub(crate) fn update_location(&self, location: MemoryLocation) {
*self.location.lock() = location;
}
pub(crate) fn update_slice(&self, slice: u32) {
self.location.lock().slice = slice;
}
pub fn update_page(&self, page: u16) {
self.location.lock().page = page;
}
pub(crate) fn location(&self) -> MemoryLocation {
*self.location.lock()
}
pub(crate) fn slice(&self) -> usize {
self.location.lock().slice as usize
}
pub(crate) fn page(&self) -> usize {
self.location.lock().page as usize
}
}
impl MemoryLocation {
pub(crate) fn new(pool: u8, page: u16, slice: u32) -> Self {
Self {
pool,
page,
slice,
init: 1,
}
}
pub(crate) fn uninit() -> Self {
Self {
pool: 0,
page: 0,
slice: 0,
init: 0,
}
}
}
impl ManagedMemoryHandle {
pub fn new() -> Self {
let value = Self::gen_id();
Self {
descriptor: Arc::new(ManagedMemoryDescriptor {
id: ManagedMemoryId { value },
location: Mutex::new(MemoryLocation::uninit()),
}),
handle_count: Arc::new(()),
}
}
pub(crate) fn descriptor(&self) -> &ManagedMemoryDescriptor {
&self.descriptor
}
pub fn can_mut(&self) -> bool {
Arc::strong_count(&self.handle_count) <= 2
}
pub fn is_free(&self) -> bool {
Arc::strong_count(&self.descriptor) <= 1
}
pub fn binding(self) -> ManagedMemoryBinding {
ManagedMemoryBinding {
descriptor: self.descriptor.clone(),
}
}
fn gen_id() -> usize {
static COUNTER: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
let value = COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
if value == usize::MAX {
core::panic!("Memory ID overflowed");
}
value
}
}
impl ManagedMemoryBinding {
pub(crate) fn descriptor(&self) -> &ManagedMemoryDescriptor {
&self.descriptor
}
}
impl Default for ManagedMemoryHandle {
fn default() -> Self {
Self::new()
}
}
impl Clone for ManagedMemoryBinding {
fn clone(&self) -> Self {
Self {
descriptor: self.descriptor.clone(),
}
}
}
impl MemoryHandle<ManagedMemoryBinding> for ManagedMemoryHandle {
fn can_mut(&self) -> bool {
self.can_mut()
}
fn binding(self) -> ManagedMemoryBinding {
self.binding()
}
}
pub fn optimal_align(shape: usize, elem_size: usize, buffer_align: usize) -> usize {
if shape == 1 {
elem_size
} else {
(shape * elem_size)
.next_power_of_two()
.clamp(16, buffer_align)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_id_mutability() {
let handle1 = ManagedMemoryHandle::new();
handle1.descriptor().update_slice(4);
assert_eq!(handle1.descriptor().slice(), 4);
let handle2 = ManagedMemoryHandle::new();
handle2
.clone()
.descriptor()
.update_location(handle1.descriptor().location());
assert_eq!(handle2.descriptor().slice(), 4);
}
#[test]
fn test_location_visible_through_shared_arc() {
let handle = ManagedMemoryHandle::new();
let handle2 = handle.clone();
let location = MemoryLocation::new(1, 2, 3);
handle.descriptor().update_location(location);
assert_eq!(handle2.descriptor().location().pool, 1);
assert_eq!(handle2.descriptor().location().page, 2);
assert_eq!(handle2.descriptor().location().slice, 3);
assert_eq!(handle2.descriptor().location().init, 1);
handle.descriptor().update_slice(42);
assert_eq!(handle2.descriptor().slice(), 42);
}
#[test]
#[cfg(feature = "runtime-std")]
fn concurrent_debug_reads_consistent_location_snapshots() {
let handle = ManagedMemoryHandle::new();
let writer = handle.clone();
let task = std::thread::spawn(move || {
for i in 1..=128_u32 {
writer.descriptor().update_location(MemoryLocation::new(i as u8, i as u16, i));
std::thread::yield_now();
}
});
for _ in 0..128 {
let _ = alloc::format!("{handle:?}");
let location = handle.descriptor().location();
assert_eq!(location.page as u32, location.slice);
assert_eq!(location.pool as u32, location.slice);
std::thread::yield_now();
}
task.join().unwrap();
assert_eq!(handle.descriptor().slice(), 128);
}
#[test]
#[cfg(feature = "runtime-std")]
fn concurrent_field_updates_do_not_overwrite_each_other() {
let handle = ManagedMemoryHandle::new();
let first = handle.clone();
let second = handle.clone();
let page_writer = std::thread::spawn(move || {
for page in 1..=128 { first.descriptor().update_page(page); }
});
let slice_writer = std::thread::spawn(move || {
for slice in 1..=128 { second.descriptor().update_slice(slice); }
});
page_writer.join().unwrap();
slice_writer.join().unwrap();
assert_eq!(handle.descriptor().page(), 128);
assert_eq!(handle.descriptor().slice(), 128);
}
}