From 4233ef5131723befb74b9bf8b980f2dde010b006 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 6 Jun 2024 18:34:58 +0200 Subject: [PATCH] [#224] Introduce RawMemory testing helper; address review findings --- .github/workflows/build-test.yml | 2 + .../complex_data_types/complex_data_types.rs | 2 +- .../container/tests/byte_string_tests.rs | 15 +--- iceoryx2-bb/container/tests/queue_tests.rs | 18 +---- iceoryx2-bb/container/tests/vec_tests.rs | 15 +--- .../tests/placement_default_tests.rs | 53 +++++------- .../elementary/src/placement_default.rs | 56 ++++++++++++- iceoryx2-bb/testing/src/lib.rs | 1 + iceoryx2-bb/testing/src/memory.rs | 80 +++++++++++++++++++ 9 files changed, 167 insertions(+), 75 deletions(-) create mode 100644 iceoryx2-bb/testing/src/memory.rs diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index b5532281c..04a2da64c 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -254,6 +254,8 @@ jobs: - uses: vmactions/freebsd-vm@v1 with: release: ${{ matrix.freebsd_version }} + mem: 8192 + copyback: false run: | ./internal/scripts/ci_prepare_freebsd.sh export PATH=$PATH:$HOME/.cargo/bin diff --git a/examples/rust/complex_data_types/complex_data_types.rs b/examples/rust/complex_data_types/complex_data_types.rs index 214f38632..0f3179413 100644 --- a/examples/rust/complex_data_types/complex_data_types.rs +++ b/examples/rust/complex_data_types/complex_data_types.rs @@ -55,7 +55,7 @@ fn main() -> Result<(), Box> { while let Iox2Event::Tick = Iox2::wait(CYCLE_TIME) { // ComplexDataType as a size of over 30MB, we need to perform a placement new - // otherwise we will encounter a StackOverflow. + // otherwise we will encounter a stack overflow in debug builds. // Therefore, we acquire an uninitialized sample, use the PlacementDefault // trait to initialize ComplexDataType in place and then populate it with data. let mut sample = publisher.loan_uninit()?; diff --git a/iceoryx2-bb/container/tests/byte_string_tests.rs b/iceoryx2-bb/container/tests/byte_string_tests.rs index 82c78fb77..a57d88f62 100644 --- a/iceoryx2-bb/container/tests/byte_string_tests.rs +++ b/iceoryx2-bb/container/tests/byte_string_tests.rs @@ -12,15 +12,13 @@ mod fixed_size_byte_string { use std::{ - alloc::{alloc, dealloc, Layout}, hash::{Hash, Hasher}, - mem::size_of, ops::DerefMut, }; use iceoryx2_bb_container::byte_string::*; use iceoryx2_bb_elementary::placement_default::PlacementDefault; - use iceoryx2_bb_testing::assert_that; + use iceoryx2_bb_testing::{assert_that, memory::RawMemory}; use std::collections::hash_map::DefaultHasher; const SUT_CAPACITY: usize = 129; @@ -506,18 +504,11 @@ mod fixed_size_byte_string { #[test] fn placement_default_works() { - let layout = Layout::new::(); - let memory = unsafe { alloc(layout) } as *mut Sut; - for i in 0..size_of::() { - unsafe { (memory as *mut u8).add(i).write(0xff) }; - } - unsafe { Sut::placement_default(memory) }; - let sut = unsafe { &mut (*memory) }; + let mut sut = RawMemory::::new_filled(0xff); + unsafe { Sut::placement_default(sut.as_mut_ptr()) }; assert_that!(sut, len 0); assert_that!(sut.push_bytes(b"hello"), is_ok); assert_that!(sut.as_bytes(), eq b"hello"); - - unsafe { dealloc(memory.cast(), layout) }; } } diff --git a/iceoryx2-bb/container/tests/queue_tests.rs b/iceoryx2-bb/container/tests/queue_tests.rs index 00843c9dd..ae08d8145 100644 --- a/iceoryx2-bb/container/tests/queue_tests.rs +++ b/iceoryx2-bb/container/tests/queue_tests.rs @@ -11,16 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT mod queue { - use std::{ - alloc::{alloc, dealloc, Layout}, - mem::size_of, - }; - use iceoryx2_bb_container::queue::*; use iceoryx2_bb_elementary::{ bump_allocator::BumpAllocator, placement_default::PlacementDefault, }; - use iceoryx2_bb_testing::{assert_that, lifetime_tracker::LifetimeTracker}; + use iceoryx2_bb_testing::{assert_that, lifetime_tracker::LifetimeTracker, memory::RawMemory}; const SUT_CAPACITY: usize = 128; type Sut = FixedSizeQueue; @@ -302,21 +297,14 @@ mod queue { #[test] fn placement_default_works() { type Sut = FixedSizeQueue; - let layout = Layout::new::(); - let memory = unsafe { alloc(layout) } as *mut Sut; - for i in 0..size_of::() { - unsafe { (memory as *mut u8).add(i).write(0xff) }; - } - unsafe { Sut::placement_default(memory) }; + let mut sut = RawMemory::::new_filled(0xff); + unsafe { Sut::placement_default(sut.as_mut_ptr()) }; - let sut = unsafe { &mut (*memory) }; assert_that!(sut, len 0); assert_that!(sut.push(123), eq true); assert_that!(sut.push(456), eq true); assert_that!(sut.pop(), eq Some(123)); assert_that!(sut.pop(), eq Some(456)); - - unsafe { dealloc(memory.cast(), layout) }; } } diff --git a/iceoryx2-bb/container/tests/vec_tests.rs b/iceoryx2-bb/container/tests/vec_tests.rs index 0a86db7f5..07276481f 100644 --- a/iceoryx2-bb/container/tests/vec_tests.rs +++ b/iceoryx2-bb/container/tests/vec_tests.rs @@ -10,15 +10,13 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use std::alloc::{alloc, dealloc, Layout}; -use std::mem::size_of; - use iceoryx2_bb_container::vec::*; use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; use iceoryx2_bb_elementary::placement_default::PlacementDefault; use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; use iceoryx2_bb_testing::assert_that; use iceoryx2_bb_testing::lifetime_tracker::LifetimeTracker; +use iceoryx2_bb_testing::memory::RawMemory; const SUT_CAPACITY: usize = 128; type Sut = FixedSizeVec; @@ -251,20 +249,13 @@ fn fixed_size_vec_pop_releases_ownership() { #[test] fn placement_default_works() { type Sut = FixedSizeVec; - let layout = Layout::new::(); - let memory = unsafe { alloc(layout) } as *mut Sut; - for i in 0..size_of::() { - unsafe { (memory as *mut u8).add(i).write(0xff) }; - } - unsafe { Sut::placement_default(memory) }; + let mut sut = RawMemory::::new_filled(0xff); + unsafe { Sut::placement_default(sut.as_mut_ptr()) }; - let sut = unsafe { &mut (*memory) }; assert_that!(sut, len 0); assert_that!(sut.push(123), eq true); assert_that!(sut.push(456), eq true); assert_that!(sut.pop(), eq Some(456)); assert_that!(sut.pop(), eq Some(123)); - - unsafe { dealloc(memory.cast(), layout) }; } diff --git a/iceoryx2-bb/derive-macros/tests/placement_default_tests.rs b/iceoryx2-bb/derive-macros/tests/placement_default_tests.rs index 1bbcb9937..e425d831b 100644 --- a/iceoryx2-bb/derive-macros/tests/placement_default_tests.rs +++ b/iceoryx2-bb/derive-macros/tests/placement_default_tests.rs @@ -12,14 +12,11 @@ #[cfg(test)] mod placement_new { - use std::{ - alloc::{alloc, dealloc, Layout}, - sync::atomic::{AtomicU64, AtomicUsize, Ordering}, - }; + use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; use iceoryx2_bb_derive_macros::PlacementDefault; use iceoryx2_bb_elementary::placement_default::PlacementDefault; - use iceoryx2_bb_testing::assert_that; + use iceoryx2_bb_testing::{assert_that, memory::RawMemory}; static DEFAULT_CTOR_COUNT: AtomicUsize = AtomicUsize::new(0); static FUU_VALUE: AtomicU64 = AtomicU64::new(0); @@ -81,15 +78,12 @@ mod placement_new { FUU_VALUE.store(123, Ordering::Relaxed); BAR_VALUE.store(456, Ordering::Relaxed); - let layout = Layout::new::(); - let memory = unsafe { alloc(layout) } as *mut NamedTestStruct; - unsafe { NamedTestStruct::placement_default(memory) }; + let memory = RawMemory::::new_zeroed(); + unsafe { NamedTestStruct::placement_default(memory.as_mut_ptr()) }; assert_that!(DEFAULT_CTOR_COUNT.load(Ordering::Relaxed), eq 3); - assert_that!(unsafe{ &*memory }.value2.0, eq FUU_VALUE.load(Ordering::Relaxed)); - assert_that!(unsafe{ &*memory }.value3.value, eq BAR_VALUE.load(Ordering::Relaxed)); - - unsafe { dealloc(memory.cast(), layout) }; + assert_that!(memory.value2.0, eq FUU_VALUE.load(Ordering::Relaxed)); + assert_that!(memory.value3.value, eq BAR_VALUE.load(Ordering::Relaxed)); } #[test] @@ -98,16 +92,13 @@ mod placement_new { FUU_VALUE.store(789, Ordering::Relaxed); BAR_VALUE.store(1337, Ordering::Relaxed); - let layout = Layout::new::(); - let memory = unsafe { alloc(layout) } as *mut UnnamedTestStruct; - unsafe { UnnamedTestStruct::placement_default(memory) }; + let memory = RawMemory::::new_zeroed(); + unsafe { UnnamedTestStruct::placement_default(memory.as_mut_ptr()) }; assert_that!(DEFAULT_CTOR_COUNT.load(Ordering::Relaxed), eq 6); - assert_that!(unsafe{ &*memory }.0.0, eq FUU_VALUE.load(Ordering::Relaxed)); - assert_that!(unsafe{ &*memory }.1.value, eq BAR_VALUE.load(Ordering::Relaxed)); - assert_that!(unsafe{ &*memory }.2.value, eq BAR_VALUE.load(Ordering::Relaxed)); - - unsafe { dealloc(memory.cast(), layout) }; + assert_that!(memory.0.0, eq FUU_VALUE.load(Ordering::Relaxed)); + assert_that!(memory.1.value, eq BAR_VALUE.load(Ordering::Relaxed)); + assert_that!(memory.2.value, eq BAR_VALUE.load(Ordering::Relaxed)); } #[test] @@ -117,15 +108,12 @@ mod placement_new { FUU_VALUE.store(4711, Ordering::Relaxed); BAR_VALUE.store(247, Ordering::Relaxed); - let layout = Layout::new::(); - let memory = unsafe { alloc(layout) } as *mut SutType; - unsafe { SutType::placement_default(memory) }; + let memory = RawMemory::::new_zeroed(); + unsafe { SutType::placement_default(memory.as_mut_ptr()) }; assert_that!(DEFAULT_CTOR_COUNT.load(Ordering::Relaxed), eq 2); - assert_that!(unsafe{ &*memory }.value1.0, eq FUU_VALUE.load(Ordering::Relaxed)); - assert_that!(unsafe{ &*memory }.value2.value, eq BAR_VALUE.load(Ordering::Relaxed)); - - unsafe { dealloc(memory.cast(), layout) }; + assert_that!(memory.value1.0, eq FUU_VALUE.load(Ordering::Relaxed)); + assert_that!(memory.value2.value, eq BAR_VALUE.load(Ordering::Relaxed)); } #[test] @@ -135,14 +123,11 @@ mod placement_new { FUU_VALUE.store(895711, Ordering::Relaxed); BAR_VALUE.store(89547, Ordering::Relaxed); - let layout = Layout::new::(); - let memory = unsafe { alloc(layout) } as *mut SutType; - unsafe { SutType::placement_default(memory) }; + let memory = RawMemory::::new_zeroed(); + unsafe { SutType::placement_default(memory.as_mut_ptr()) }; assert_that!(DEFAULT_CTOR_COUNT.load(Ordering::Relaxed), eq 2); - assert_that!(unsafe{ &*memory }.0.0, eq FUU_VALUE.load(Ordering::Relaxed)); - assert_that!(unsafe{ &*memory }.1.value, eq BAR_VALUE.load(Ordering::Relaxed)); - - unsafe { dealloc(memory.cast(), layout) }; + assert_that!(memory.0.0, eq FUU_VALUE.load(Ordering::Relaxed)); + assert_that!(memory.1.value, eq BAR_VALUE.load(Ordering::Relaxed)); } } diff --git a/iceoryx2-bb/elementary/src/placement_default.rs b/iceoryx2-bb/elementary/src/placement_default.rs index 4ab1389d7..f2839a127 100644 --- a/iceoryx2-bb/elementary/src/placement_default.rs +++ b/iceoryx2-bb/elementary/src/placement_default.rs @@ -13,6 +13,8 @@ //! Trait to perform placement new construction on a given pointer via [`Default::default()`]. //! See [`PlacementDefault`] for example. +use std::mem::MaybeUninit; + use iceoryx2_pal_concurrency_sync::iox_atomic::*; /// A trait that allows types to perform a placement new based on their @@ -98,6 +100,15 @@ Impl!(IoxAtomicI64); Impl!(IoxAtomicIsize); Impl!(IoxAtomicUsize); +impl PlacementDefault for [T] { + unsafe fn placement_default(ptr: *mut Self) { + let ptr = ptr as *mut [MaybeUninit]; + for i in (*ptr).iter_mut() { + PlacementDefault::placement_default(i.as_mut_ptr()); + } + } +} + impl PlacementDefault for [T; CAPACITY] { unsafe fn placement_default(ptr: *mut Self) { for i in 0..CAPACITY { @@ -106,13 +117,56 @@ impl PlacementDefault for [T; CAPACI } } -impl PlacementDefault for (T,) { +impl PlacementDefault for (T1,) { unsafe fn placement_default(ptr: *mut Self) { let ptr = core::ptr::addr_of_mut!((*ptr).0); PlacementDefault::placement_default(ptr) } } +impl PlacementDefault for (T1, T2) { + unsafe fn placement_default(ptr: *mut Self) { + let elem = core::ptr::addr_of_mut!((*ptr).0); + PlacementDefault::placement_default(elem); + + let elem = core::ptr::addr_of_mut!((*ptr).1); + PlacementDefault::placement_default(elem) + } +} + +impl PlacementDefault + for (T1, T2, T3) +{ + unsafe fn placement_default(ptr: *mut Self) { + let elem = core::ptr::addr_of_mut!((*ptr).0); + PlacementDefault::placement_default(elem); + + let elem = core::ptr::addr_of_mut!((*ptr).1); + PlacementDefault::placement_default(elem); + + let elem = core::ptr::addr_of_mut!((*ptr).2); + PlacementDefault::placement_default(elem) + } +} + +impl + PlacementDefault for (T1, T2, T3, T4) +{ + unsafe fn placement_default(ptr: *mut Self) { + let elem = core::ptr::addr_of_mut!((*ptr).0); + PlacementDefault::placement_default(elem); + + let elem = core::ptr::addr_of_mut!((*ptr).1); + PlacementDefault::placement_default(elem); + + let elem = core::ptr::addr_of_mut!((*ptr).2); + PlacementDefault::placement_default(elem); + + let elem = core::ptr::addr_of_mut!((*ptr).3); + PlacementDefault::placement_default(elem) + } +} + impl PlacementDefault for Option { unsafe fn placement_default(ptr: *mut Self) { ptr.write(None) diff --git a/iceoryx2-bb/testing/src/lib.rs b/iceoryx2-bb/testing/src/lib.rs index 0606e24b6..e49d84289 100644 --- a/iceoryx2-bb/testing/src/lib.rs +++ b/iceoryx2-bb/testing/src/lib.rs @@ -13,6 +13,7 @@ #[macro_use] pub mod assert; pub mod lifetime_tracker; +pub mod memory; pub mod watchdog; #[macro_export(local_inner_macros)] diff --git a/iceoryx2-bb/testing/src/memory.rs b/iceoryx2-bb/testing/src/memory.rs new file mode 100644 index 000000000..e2420b1af --- /dev/null +++ b/iceoryx2-bb/testing/src/memory.rs @@ -0,0 +1,80 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use std::{ + alloc::{alloc, dealloc, Layout}, + ops::{Deref, DerefMut}, +}; + +/// Allocates uninitialized memory on the heap. When it goes out of scope the memory is released. +/// The user has to ensure that the memory is initialized. +pub struct RawMemory { + memory: *mut T, +} + +impl Drop for RawMemory { + fn drop(&mut self) { + unsafe { dealloc(self.memory.cast(), Layout::new::()) } + } +} + +impl Deref for RawMemory { + type Target = T; + fn deref(&self) -> &Self::Target { + unsafe { &*self.memory } + } +} + +impl DerefMut for RawMemory { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.memory } + } +} + +impl RawMemory { + pub fn new u8>(mut fill: F) -> Self { + let layout = Layout::new::(); + let memory = unsafe { alloc(layout) } as *mut u8; + + for i in 0..layout.size() { + unsafe { memory.add(i).write(fill(i)) } + } + + Self { + memory: memory.cast(), + } + } + + pub fn new_zeroed() -> Self { + Self::new(|_| 0u8) + } + + pub fn new_filled(value: u8) -> Self { + Self::new(|_| value) + } + + pub fn as_ptr(&self) -> *const T { + self.memory + } + + pub fn as_mut_ptr(&self) -> *mut T { + self.memory + } + + pub fn as_slice(&self) -> &[u8] { + unsafe { core::slice::from_raw_parts(self.memory.cast(), core::mem::size_of::()) } + } + + pub fn as_slice_mut(&self) -> &mut [u8] { + unsafe { core::slice::from_raw_parts_mut(self.memory.cast(), core::mem::size_of::()) } + } +}