improve sequence counters

This commit is contained in:
Robin Mueller
2025-09-09 11:51:59 +02:00
parent 20403bda32
commit e8a01dc6b2
2 changed files with 123 additions and 67 deletions

View File

@@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added PUS A legacy support for telecommands inside the `ecss.tc_pus_a` module - Added PUS A legacy support for telecommands inside the `ecss.tc_pus_a` module
- Added `SequenceCounter::increment_mut` and `SequenceCounter::get_and_increment_mut` - Added `SequenceCounter::increment_mut` and `SequenceCounter::get_and_increment_mut`
- Implemented `SequenceCounter` for `Atomic` unsigned types and references of them
# [v0.15.0] 2025-07-18 # [v0.15.0] 2025-07-18

View File

@@ -1,8 +1,6 @@
use crate::MAX_SEQ_COUNT; use crate::MAX_SEQ_COUNT;
use core::cell::Cell; use core::cell::Cell;
use paste::paste; use paste::paste;
#[cfg(feature = "std")]
pub use stdmod::*;
/// Core trait for objects which can provide a sequence count. /// Core trait for objects which can provide a sequence count.
/// ///
@@ -120,45 +118,102 @@ impl SequenceCounter for SequenceCounterCcsdsSimple {
} }
} }
#[cfg(feature = "std")] impl SequenceCounter for core::sync::atomic::AtomicU8 {
pub mod stdmod { type Raw = u8;
use super::*;
use std::sync::{Arc, Mutex};
macro_rules! sync_clonable_seq_counter_impl { const MAX_BIT_WIDTH: usize = 8;
fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed)
}
fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
}
impl SequenceCounter for core::sync::atomic::AtomicU16 {
type Raw = u16;
const MAX_BIT_WIDTH: usize = 16;
fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed)
}
fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
}
impl SequenceCounter for core::sync::atomic::AtomicU32 {
type Raw = u32;
const MAX_BIT_WIDTH: usize = 32;
fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed)
}
fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
}
impl SequenceCounter for core::sync::atomic::AtomicU64 {
type Raw = u64;
const MAX_BIT_WIDTH: usize = 64;
fn get(&self) -> Self::Raw {
self.load(core::sync::atomic::Ordering::Relaxed)
}
fn increment(&self) {
self.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
}
impl<T: SequenceCounter + ?Sized> SequenceCounter for &T {
type Raw = T::Raw;
const MAX_BIT_WIDTH: usize = T::MAX_BIT_WIDTH;
fn get(&self) -> Self::Raw {
(**self).get()
}
fn increment(&self) {
(**self).increment()
}
}
macro_rules! sync_clonable_seq_counter_impl {
($($ty: ident,)+) => { ($($ty: ident,)+) => {
$(paste! { $(paste! {
/// These sequence counters can be shared between threads and can also be
/// configured to wrap around at specified maximum values. Please note that /// This can be used if a custom wrap value is required when using a thread-safe
/// that the API provided by this class will not panic und [Mutex] lock errors, /// atomic based sequence counter.
/// but it will yield 0 for the getter functions. #[derive(Debug)]
#[derive(Clone, Default)] pub struct [<SequenceCounterSyncCustomWrap $ty:upper>] {
pub struct [<SequenceCounterSync $ty:upper>] { seq_count: core::sync::atomic::[<Atomic $ty:upper>],
seq_count: Arc<Mutex<$ty>>, max_val: $ty,
max_val: $ty
} }
impl [<SequenceCounterSync $ty:upper>] { impl [<SequenceCounterSyncCustomWrap $ty:upper>] {
pub fn new() -> Self { pub fn new(max_val: $ty) -> Self {
Self::new_with_max_val($ty::MAX)
}
pub fn new_with_max_val(max_val: $ty) -> Self {
Self { Self {
seq_count: Arc::default(), seq_count: core::sync::atomic::[<Atomic $ty:upper>]::new(0),
max_val max_val,
} }
} }
} }
impl SequenceCounter for [<SequenceCounterSync $ty:upper>] {
impl SequenceCounter for [<SequenceCounterSyncCustomWrap $ty:upper>] {
type Raw = $ty; type Raw = $ty;
const MAX_BIT_WIDTH: usize = core::mem::size_of::<Self::Raw>() * 8; const MAX_BIT_WIDTH: usize = core::mem::size_of::<Self::Raw>() * 8;
fn get(&self) -> $ty { fn get(&self) -> $ty {
match self.seq_count.lock() { self.seq_count.load(core::sync::atomic::Ordering::Relaxed)
Ok(counter) => *counter,
Err(_) => 0
}
} }
fn increment(&self) { fn increment(&self) {
@@ -166,30 +221,30 @@ pub mod stdmod {
} }
fn get_and_increment(&self) -> $ty { fn get_and_increment(&self) -> $ty {
match self.seq_count.lock() { self.seq_count.fetch_update(
Ok(mut counter) => { core::sync::atomic::Ordering::Relaxed,
let val = *counter; core::sync::atomic::Ordering::Relaxed,
if val == self.max_val { |cur| {
*counter = 0; // compute the next value, wrapping at MAX_VAL
} else { let next = if cur == self.max_val { 0 } else { cur + 1 };
*counter += 1; Some(next)
} },
val ).unwrap()
}
Err(_) => 0,
}
} }
} }
})+ })+
} }
}
sync_clonable_seq_counter_impl!(u8, u16, u32, u64,);
} }
sync_clonable_seq_counter_impl!(u8, u16, u32, u64,);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use core::sync::atomic::AtomicU8;
use crate::seq_count::{ use crate::seq_count::{
SequenceCounter, SequenceCounterCcsdsSimple, SequenceCounterSimple, SequenceCounterSyncU8, SequenceCounter, SequenceCounterCcsdsSimple, SequenceCounterSimple,
SequenceCounterSyncCustomWrapU8,
}; };
use crate::MAX_SEQ_COUNT; use crate::MAX_SEQ_COUNT;
@@ -231,7 +286,7 @@ mod tests {
#[test] #[test]
fn test_atomic_ref_counters() { fn test_atomic_ref_counters() {
let sync_u8_counter = SequenceCounterSyncU8::new(); let sync_u8_counter = AtomicU8::new(0);
assert_eq!(sync_u8_counter.get(), 0); assert_eq!(sync_u8_counter.get(), 0);
assert_eq!(sync_u8_counter.get_and_increment(), 0); assert_eq!(sync_u8_counter.get_and_increment(), 0);
assert_eq!(sync_u8_counter.get_and_increment(), 1); assert_eq!(sync_u8_counter.get_and_increment(), 1);
@@ -240,7 +295,7 @@ mod tests {
#[test] #[test]
fn test_atomic_ref_counters_overflow() { fn test_atomic_ref_counters_overflow() {
let sync_u8_counter = SequenceCounterSyncU8::new(); let sync_u8_counter = AtomicU8::new(0);
for _ in 0..u8::MAX as u16 + 1 { for _ in 0..u8::MAX as u16 + 1 {
sync_u8_counter.increment(); sync_u8_counter.increment();
} }
@@ -249,7 +304,7 @@ mod tests {
#[test] #[test]
fn test_atomic_ref_counters_overflow_custom_max_val() { fn test_atomic_ref_counters_overflow_custom_max_val() {
let sync_u8_counter = SequenceCounterSyncU8::new_with_max_val(128); let sync_u8_counter = SequenceCounterSyncCustomWrapU8::new(128);
for _ in 0..129 { for _ in 0..129 {
sync_u8_counter.increment(); sync_u8_counter.increment();
} }