diff --git a/ROADMAP.md b/ROADMAP.md index bec0acf75..9409f1ce0 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -7,6 +7,19 @@ * [ ] `#![no_std]` on `stable` on all tier 1 platforms * [ ] completely dynamic setup with dynamic shared memory +## Shared Memory Container & Types + +* [ ] Make `iceoryx2_bb_container` public with announcement +* [ ] Create and document dynamic size container concept for shared memory and apply it + to all existing containers: `ByteString`, `Vec`, `Queue` + * Open Question: How can these containers be cloned, copied? +* [ ] Introduce additional containers: `HashMap`, `Tree`, `Set`, `List` +* [ ] Introduce elementary types, look into: `simple-si-units` crate + * Add types like: memory size, percentage, strict percentage (0..100), data throughput, resolution + (further types found in informatics) +* [ ] Add `derive` proc macro to ensure that only shm compatible types can be + transferred via zero-copy + ## Language Bindings * [ ] C diff --git a/doc/release-notes/iceoryx2-unreleased.md b/doc/release-notes/iceoryx2-unreleased.md index e8a510de1..918d3b8c2 100644 --- a/doc/release-notes/iceoryx2-unreleased.md +++ b/doc/release-notes/iceoryx2-unreleased.md @@ -22,7 +22,7 @@ ### New API features - * Example [#1](https://github.com/eclipse-iceoryx/iceoryx2/issues/1) + * Add `FixedSizeByteString::from_bytes_truncated` [#56](https://github.com/eclipse-iceoryx/iceoryx2/issues/56) ### API Breaking Changes diff --git a/iceoryx2-bb/container/src/byte_string.rs b/iceoryx2-bb/container/src/byte_string.rs index 2f1fb638c..77b465ca7 100644 --- a/iceoryx2-bb/container/src/byte_string.rs +++ b/iceoryx2-bb/container/src/byte_string.rs @@ -237,6 +237,22 @@ impl FixedSizeByteString { Ok(new_self) } + /// Creates a new [`FixedSizeByteString`] from a byte slice. If the byte slice does not fit + /// into the [`FixedSizeByteString`] it will be truncated. + pub fn from_bytes_truncated(bytes: &[u8]) -> Self { + let mut new_self = Self::new(); + new_self.len = std::cmp::min(bytes.len(), CAPACITY); + for (i, byte) in bytes.iter().enumerate().take(new_self.len) { + new_self.data[i].write(*byte); + } + + if new_self.len < CAPACITY { + new_self.data[new_self.len].write(0); + } + + new_self + } + /// Creates a new byte string from a given null-terminated string /// /// # Safety diff --git a/iceoryx2-bb/container/tests/byte_string_tests.rs b/iceoryx2-bb/container/tests/byte_string_tests.rs index 0e58d6efd..1c1e9b633 100644 --- a/iceoryx2-bb/container/tests/byte_string_tests.rs +++ b/iceoryx2-bb/container/tests/byte_string_tests.rs @@ -47,19 +47,40 @@ fn fixed_size_byte_string_from_bytes_works() { assert_that!(sut.pop(), eq Some(b'd')); } +#[test] +fn fixed_size_byte_string_from_bytes_truncated_works() { + let sut = Sut::from_bytes(b"bonjour world"); + assert_that!(sut, is_ok); + let mut sut = sut.unwrap(); + + assert_that!(sut, is_not_empty); + assert_that!(sut.is_full(), eq false); + assert_that!(sut, len 13); + assert_that!(sut, eq b"bonjour world"); + assert_that!(sut, ne b"bonjour world! woo"); + assert_that!(sut.as_bytes(), eq b"bonjour world"); + assert_that!(sut.as_mut_bytes(), eq b"bonjour world"); + assert_that!(sut.as_bytes_with_nul(), eq b"bonjour world\0"); + assert_that!(sut.pop(), eq Some(b'd')); +} + #[test] fn fixed_size_byte_string_from_byte_slice_works() { - let mut sut = Sut::from(b"hello world!"); + let sut = FixedSizeByteString::<5>::from_bytes_truncated(b"hell"); assert_that!(sut, is_not_empty); assert_that!(sut.is_full(), eq false); - assert_that!(sut, len 12); - assert_that!(sut, eq b"hello world!"); - assert_that!(sut, ne b"hello world! woo"); - assert_that!(sut.as_bytes(), eq b"hello world!"); - assert_that!(sut.as_mut_bytes(), eq b"hello world!"); - assert_that!(sut.as_bytes_with_nul(), eq b"hello world!\0"); - assert_that!(sut.pop(), eq Some(b'!')); + assert_that!(sut, len 4); + assert_that!(sut, eq b"hell"); + assert_that!(sut.as_bytes_with_nul(), eq b"hell\0"); + + let sut = FixedSizeByteString::<5>::from_bytes_truncated(b"hello world"); + + assert_that!(sut, is_not_empty); + assert_that!(sut.is_full(), eq true); + assert_that!(sut, len 5); + assert_that!(sut, eq b"hello"); + assert_that!(sut.as_bytes_with_nul(), eq b"hello\0"); } #[test] diff --git a/iceoryx2-bb/posix/src/mutex.rs b/iceoryx2-bb/posix/src/mutex.rs index a1f2725cf..13315c0f5 100644 --- a/iceoryx2-bb/posix/src/mutex.rs +++ b/iceoryx2-bb/posix/src/mutex.rs @@ -266,7 +266,12 @@ pub enum MutexThreadTerminationBehavior { /// mutex owning /// thread/process dies the mutex is put into an inconsistent state which can be recovered with /// [`Mutex::make_consistent()`]. The inconsistent state is detected by the next instance which - /// calls [`Mutex::lock()`], [`Mutex::try_lock()`] or [`Mutex::timed_lock()`]. + /// calls [`Mutex::try_lock()`] or [`Mutex::timed_lock()`]. + /// + /// **Important:** If the owner dies after another thread has already locked the [`Mutex`] it + /// may become impossible to recover the [`Mutex`]. Therefore, this feature should be used + /// only in combination with either [`Mutex::try_lock`] or [`Mutex::timed_lock()`] and + /// never with [`Mutex::lock()`]. /// /// This is also known as robust mutex. ReleaseWhenLocked = posix::PTHREAD_MUTEX_ROBUST, diff --git a/iceoryx2-bb/posix/tests/mutex_tests.rs b/iceoryx2-bb/posix/tests/mutex_tests.rs index 9b15f3b56..cfec622c8 100644 --- a/iceoryx2-bb/posix/tests/mutex_tests.rs +++ b/iceoryx2-bb/posix/tests/mutex_tests.rs @@ -405,14 +405,16 @@ fn mutex_can_be_recovered_when_thread_died() { }); }); - let guard = sut.lock(); - assert_that!(guard, is_err); - match guard.as_ref().err().as_ref().unwrap() { - MutexLockError::LockAcquiredButOwnerDied(_) => (), - _ => assert_that!(true, eq false), + loop { + let guard = sut.try_lock(); + + if guard.is_ok() { + assert_that!(guard.as_ref().unwrap(), is_none); + } else if let Err(MutexLockError::LockAcquiredButOwnerDied(_)) = guard { + sut.make_consistent(); + break; + } } - sut.make_consistent(); - drop(guard); let guard = sut.try_lock(); assert_that!(guard, is_ok);