Skip to content

Commit

Permalink
[eclipse-iceoryx#497] Add chunk counting
Browse files Browse the repository at this point in the history
  • Loading branch information
elfenpiff committed Nov 12, 2024
1 parent 8305d56 commit f71e100
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 26 deletions.
134 changes: 113 additions & 21 deletions iceoryx2-cal/src/resizable_shared_memory/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ use crate::shm_allocator::ShmAllocationError;
use iceoryx2_bb_container::semantic_string::SemanticString;
use iceoryx2_bb_container::slotmap::{SlotMap, SlotMapKey};
use iceoryx2_bb_elementary::allocator::AllocationError;
use iceoryx2_bb_log::fail;
use iceoryx2_bb_log::fatal_panic;
use iceoryx2_bb_log::{fail, warn};
use iceoryx2_bb_system_types::file_name::FileName;
use iceoryx2_bb_system_types::path::Path;
use iceoryx2_pal_concurrency_sync::iox_atomic::IoxAtomicBool;
use iceoryx2_pal_concurrency_sync::iox_atomic::{IoxAtomicBool, IoxAtomicU64};

use super::{
NamedConcept, NamedConceptBuilder, NamedConceptDoesExistError, NamedConceptListError,
Expand All @@ -48,6 +48,59 @@ struct BuilderConfig<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> {
max_number_of_chunks_hint: usize,
}

#[derive(Debug)]
struct InternalState<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> {
builder_config: BuilderConfig<Allocator, Shm>,
shared_memory_map: SlotMap<ShmEntry<Allocator, Shm>>,
current_idx: SlotMapKey,
}

trait UpdateSegmentId {
fn update(&mut self, updated_value: Self);
}

impl UpdateSegmentId for SlotMapKey {
fn update(&mut self, updated_value: Self) {
if self.value() < updated_value.value() {
*self = updated_value;
}
}
}

#[derive(Debug)]
struct ShmEntry<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> {
shm: Shm,
chunk_count: IoxAtomicU64,
_data: PhantomData<Allocator>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ShmEntryState {
Empty,
NonEmpty,
}

impl<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> ShmEntry<Allocator, Shm> {
fn new(shm: Shm) -> Self {
Self {
shm,
chunk_count: IoxAtomicU64::new(0),
_data: PhantomData,
}
}

fn register_offset(&self) {
self.chunk_count.fetch_add(1, Ordering::Relaxed);
}

fn unregister_offset(&self) -> ShmEntryState {
match self.chunk_count.fetch_sub(1, Ordering::Relaxed) {
1 => ShmEntryState::Empty,
_ => ShmEntryState::NonEmpty,
}
}
}

#[derive(Debug)]
pub struct DynamicBuilder<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>>
where
Expand Down Expand Up @@ -131,7 +184,7 @@ where

let mut shared_memory_map = SlotMap::new(MAX_DATASEGMENTS);
let current_idx = shared_memory_map
.insert(shm)
.insert(ShmEntry::new(shm))
.expect("MAX_DATASEGMENTS is greater or equal 1");

Ok(DynamicMemory {
Expand All @@ -151,13 +204,14 @@ where
"Unable to open ResizableSharedMemoryView since the underlying shared memory could not be opened.");

let mut shared_memory_map = SlotMap::new(MAX_DATASEGMENTS);
shared_memory_map
.insert(shm)
let current_idx = shared_memory_map
.insert(ShmEntry::new(shm))
.expect("MAX_DATASEGMENTS is greater or equal 1");

Ok(DynamicView {
builder_config: self.config,
shared_memory_map,
current_idx,
_data: PhantomData,
})
}
Expand All @@ -166,14 +220,15 @@ where
#[derive(Debug)]
pub struct DynamicView<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> {
builder_config: BuilderConfig<Allocator, Shm>,
shared_memory_map: SlotMap<Shm>,
shared_memory_map: SlotMap<ShmEntry<Allocator, Shm>>,
current_idx: SlotMapKey,
_data: PhantomData<Allocator>,
}

impl<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>>
ResizableSharedMemoryView<Allocator, Shm> for DynamicView<Allocator, Shm>
{
fn translate_offset(
fn register_and_translate_offset(
&mut self,
offset: PointerOffset,
) -> Result<*const u8, SharedMemoryOpenError> {
Expand All @@ -188,21 +243,41 @@ impl<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>>
when DynamicMemory::open_segment(&self.builder_config, segment_id),
"{msg} {:?} since the corresponding shared memory segment could not be opened.", offset);
let payload_start_address = shm.payload_start_address();
self.shared_memory_map.insert_at(key, shm);
let entry = ShmEntry::new(shm);
entry.register_offset();
self.shared_memory_map.insert_at(key, entry);
self.current_idx.update(key);
payload_start_address
}
Some(shm) => shm.payload_start_address(),
Some(entry) => {
entry.register_offset();
entry.shm.payload_start_address()
}
};

Ok((offset + payload_start_address) as *const u8)
}
}

#[derive(Debug)]
struct InternalState<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> {
builder_config: BuilderConfig<Allocator, Shm>,
shared_memory_map: SlotMap<Shm>,
current_idx: SlotMapKey,
fn unregister_offset(&mut self, offset: PointerOffset) {
let segment_id = offset.segment_id();
let key = SlotMapKey::new(segment_id as usize);

match self.shared_memory_map.get(key) {
Some(entry) => {
if entry.unregister_offset() == ShmEntryState::Empty && self.current_idx != key {
self.shared_memory_map.remove(key);
}
}
None => {
warn!(from self,
"Unable to unregister offset {:?} since the segment id is not mapped.", offset);
}
}
}

fn number_of_active_segments(&self) -> usize {
self.shared_memory_map.len()
}
}

#[derive(Debug)]
Expand Down Expand Up @@ -253,6 +328,10 @@ impl<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> DynamicMemory<Alloca
unsafe { &mut *self.state.get() }
}

fn state(&self) -> &InternalState<Allocator, Shm> {
unsafe { &*self.state.get() }
}

fn create_segment(
config: &BuilderConfig<Allocator, Shm>,
segment_id: u8,
Expand Down Expand Up @@ -301,8 +380,8 @@ impl<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> DynamicMemory<Alloca
)?;

let key = SlotMapKey::new(segment_id as usize);
state.shared_memory_map.insert_at(key, shm);
state.current_idx = key;
state.shared_memory_map.insert_at(key, ShmEntry::new(shm));
state.current_idx.update(key);

Ok(())
}
Expand All @@ -316,19 +395,24 @@ where
type Builder = DynamicBuilder<Allocator, Shm>;
type View = DynamicView<Allocator, Shm>;

fn number_of_active_segments(&self) -> usize {
self.state().shared_memory_map.len()
}

fn allocate(&self, layout: Layout) -> Result<ShmPointer, ResizableShmAllocationError> {
let state = self.state_mut();

loop {
match state.shared_memory_map.get(state.current_idx) {
Some(ref shm) => match shm.allocate(layout) {
Some(ref entry) => match entry.shm.allocate(layout) {
Ok(mut ptr) => {
entry.register_offset();
ptr.offset.set_segment_id(state.current_idx.value() as u8);
return Ok(ptr);
}
Err(ShmAllocationError::AllocationError(AllocationError::OutOfMemory)) => {
self.create_resized_segment(
shm,
&entry.shm,
layout,
state.current_idx.value() as u8 + 1,
)?;
Expand All @@ -342,9 +426,17 @@ where
}

unsafe fn deallocate(&self, offset: PointerOffset, layout: Layout) {
let segment_id = SlotMapKey::new(offset.segment_id() as usize);
let state = self.state_mut();
match state.shared_memory_map.get(state.current_idx) {
Some(shm) => shm.deallocate(offset, layout),
match state.shared_memory_map.get(segment_id) {
Some(entry) => {
entry.shm.deallocate(offset, layout);
if entry.unregister_offset() == ShmEntryState::Empty
&& segment_id != state.current_idx
{
state.shared_memory_map.remove(segment_id);
}
}
None => fatal_panic!(from self,
"This should never happen! Current shared memory segment is not available!"),
}
Expand Down
8 changes: 7 additions & 1 deletion iceoryx2-cal/src/resizable_shared_memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,14 @@ pub trait ResizableSharedMemoryBuilder<
}

pub trait ResizableSharedMemoryView<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> {
fn translate_offset(
fn register_and_translate_offset(
&mut self,
offset: PointerOffset,
) -> Result<*const u8, SharedMemoryOpenError>;

fn unregister_offset(&mut self, offset: PointerOffset);

fn number_of_active_segments(&self) -> usize;
}

pub trait ResizableSharedMemory<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>>:
Expand All @@ -75,6 +79,8 @@ pub trait ResizableSharedMemory<Allocator: ShmAllocator, Shm: SharedMemory<Alloc
type Builder: ResizableSharedMemoryBuilder<Allocator, Shm, Self, Self::View>;
type View: ResizableSharedMemoryView<Allocator, Shm>;

fn number_of_active_segments(&self) -> usize;

fn allocate(
&self,
layout: std::alloc::Layout,
Expand Down
61 changes: 57 additions & 4 deletions iceoryx2-cal/tests/resizable_shared_memory_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
mod resizable_shared_memory {
use std::alloc::Layout;

use iceoryx2_bb_log::set_log_level;
use iceoryx2_bb_testing::assert_that;
use iceoryx2_cal::named_concept::*;
use iceoryx2_cal::resizable_shared_memory::{self, *};
Expand Down Expand Up @@ -52,7 +51,9 @@ mod resizable_shared_memory {

unsafe { (ptr_creator.data_ptr as *mut u64).write(test_value_1) };

let ptr_view = sut_viewer.translate_offset(ptr_creator.offset).unwrap() as *const u64;
let ptr_view = sut_viewer
.register_and_translate_offset(ptr_creator.offset)
.unwrap() as *const u64;

assert_that!(unsafe{ *ptr_view }, eq test_value_1);
unsafe { (ptr_creator.data_ptr as *mut u64).write(test_value_2) };
Expand Down Expand Up @@ -87,13 +88,65 @@ mod resizable_shared_memory {
unsafe { (ptr_creator_1.data_ptr as *mut u64).write(test_value_1) };
unsafe { (ptr_creator_2.data_ptr as *mut u64).write(test_value_2) };

let ptr_view_1 = sut_viewer.translate_offset(ptr_creator_1.offset).unwrap() as *const u64;
let ptr_view_2 = sut_viewer.translate_offset(ptr_creator_2.offset).unwrap() as *const u64;
let ptr_view_1 = sut_viewer
.register_and_translate_offset(ptr_creator_1.offset)
.unwrap() as *const u64;
let ptr_view_2 = sut_viewer
.register_and_translate_offset(ptr_creator_2.offset)
.unwrap() as *const u64;

assert_that!(unsafe{ *ptr_view_1 }, eq test_value_1);
assert_that!(unsafe{ *ptr_view_2 }, eq test_value_2);
}

#[test]
fn deallocate_removes_unused_segments_on_creator_side<
Shm: SharedMemory<DefaultAllocator>,
Sut: ResizableSharedMemory<DefaultAllocator, Shm>,
>() {
let storage_name = generate_name();
let config = generate_isolated_config::<Sut>();

let sut_creator = Sut::Builder::new(&storage_name)
.config(&config)
.max_number_of_chunks_hint(1)
.create()
.unwrap();

let ptr_creator_1 = sut_creator.allocate(Layout::new::<u64>()).unwrap();
assert_that!(sut_creator.number_of_active_segments(), eq 1);

let _ptr_creator_2 = sut_creator.allocate(Layout::new::<u64>()).unwrap();
assert_that!(sut_creator.number_of_active_segments(), eq 2);

unsafe { sut_creator.deallocate(ptr_creator_1.offset, Layout::new::<u64>()) };
assert_that!(sut_creator.number_of_active_segments(), eq 1);
}

#[test]
fn unregister_removes_unused_segments_on_viewer_side<
Shm: SharedMemory<DefaultAllocator>,
Sut: ResizableSharedMemory<DefaultAllocator, Shm>,
>() {
let storage_name = generate_name();
let config = generate_isolated_config::<Sut>();

let sut_creator = Sut::Builder::new(&storage_name)
.config(&config)
.max_number_of_chunks_hint(1)
.create()
.unwrap();

let ptr_creator_1 = sut_creator.allocate(Layout::new::<u64>()).unwrap();
assert_that!(sut_creator.number_of_active_segments(), eq 1);

let _ptr_creator_2 = sut_creator.allocate(Layout::new::<u64>()).unwrap();
assert_that!(sut_creator.number_of_active_segments(), eq 2);

unsafe { sut_creator.deallocate(ptr_creator_1.offset, Layout::new::<u64>()) };
assert_that!(sut_creator.number_of_active_segments(), eq 1);
}

#[instantiate_tests(<iceoryx2_cal::shared_memory::posix::Memory<DefaultAllocator>, resizable_shared_memory::dynamic::DynamicMemory<DefaultAllocator, iceoryx2_cal::shared_memory::posix::Memory<DefaultAllocator>>>)]
mod posix {}

Expand Down

0 comments on commit f71e100

Please sign in to comment.