diff --git a/satrs-book/README.md b/satrs-book/README.md index c6fcf81..64321fb 100644 --- a/satrs-book/README.md +++ b/satrs-book/README.md @@ -5,7 +5,14 @@ High-level documentation of the [sat-rs project](https://absatsw.irs.uni-stuttga ## Building -If you have not done so, install `mdbook` using `cargo install mdbook --locked`. +If you have not done so, install the pre-requisites first: + +```sh +cargo install mdbook --locked +cargo install mdbook-linkcheck --locked +``` + +After that, you can build the book with: ```sh mdbook build diff --git a/satrs-book/src/constrained-systems.md b/satrs-book/src/constrained-systems.md index 7890c47..c032cd0 100644 --- a/satrs-book/src/constrained-systems.md +++ b/satrs-book/src/constrained-systems.md @@ -11,7 +11,8 @@ time where the OBSW might be running on Linux based systems with hundreds of MBs A useful pattern used commonly in space systems is to limit heap allocations to program initialization time and avoid frequent run-time allocations. This prevents issues like -running out of memory (something even Rust can not protect from) or heap fragmentation. +running out of memory (something even Rust can not protect from) or heap fragmentation on systems +without a MMU. # Using pre-allocated pool structures @@ -24,6 +25,12 @@ For example, a very small telecommand (TC) pool might look like this: ![Example Pool](images/pools/static-pools.png) +The core of the pool abstractions is the +[PoolProvider trait](https://docs.rs/satrs-core/0.1.0-alpha.3/satrs_core/pool/trait.PoolProvider.html). +This trait specifies the general API a pool structure should have without making assumption +of how the data is stored. + +This trait is implemented by a static memory pool implementation. The code to generate this static pool would look like this: ```rust @@ -39,21 +46,27 @@ let tc_pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![ It should be noted that the buckets only show the maximum size of data being stored inside them. The store will keep a separate structure to track the actual size of the data being stored. - - - A TC entry inside this pool has a store address which can then be sent around without having to dynamically allocate memory. The same principle can also be applied to the telemetry (TM) and inter-process communication (IPC) data. +You can read + +- [`StaticPoolConfig` API](https://docs.rs/satrs-core/0.1.0-alpha.3/satrs_core/pool/struct.StaticPoolConfig.html) +- [`StaticMemoryPool` API](https://docs.rs/satrs-core/0.1.0-alpha.3/satrs_core/pool/struct.StaticMemoryPool.html) + +for more details. + +In the future, optimized pool structures which use standard containers or are +[`Sync`](https://doc.rust-lang.org/std/marker/trait.Sync.html) by default might be added as well. + # Using special crates to prevent smaller allocations Another common way to use the heap on host systems is using containers like `String` and `Vec` to work with data where the size is not known beforehand. The most common solution for embedded systems is to determine the maximum expected size and then use a pre-allocated `u8` buffer and a size variable. Alternatively, you can use the following crates for more convenience or a smart -behaviour which at the very least reduce heap allocations: +behaviour which at the very least reduces heap allocations: 1. [`smallvec`](https://docs.rs/smallvec/latest/smallvec/). 2. [`arrayvec`](https://docs.rs/arrayvec/latest/arrayvec/index.html) which also contains an diff --git a/satrs-core/src/pool.rs b/satrs-core/src/pool.rs index 07fc863..fb59065 100644 --- a/satrs-core/src/pool.rs +++ b/satrs-core/src/pool.rs @@ -203,8 +203,12 @@ impl Error for StoreError { } } -/// Generic trait for pool providers where the data can be modified and read in-place. This -/// generally means that a shared pool structure has to be wrapped inside a lock structure. +/// Generic trait for pool providers which provide memory pools for variable sized data. +/// +/// It specifies a basic API to [Self::add], [Self::modify], [Self::read] and [Self::delete] data +/// in the store at its core. The API was designed so internal optimizations can be performed +/// more easily and that is is also possible to make the pool structure [Sync] without the whole +/// pool structure being wrapped inside a lock. pub trait PoolProvider { /// Add new data to the pool. The provider should attempt to reserve a memory block with the /// appropriate size and then copy the given data to the block. Yields a [StoreAddr] which can @@ -251,6 +255,7 @@ pub trait PoolProvider { } } +/// Extension trait which adds guarded pool access classes. pub trait PoolProviderWithGuards: PoolProvider { /// This function behaves like [PoolProvider::read], but consumes the provided address /// and returns a RAII conformant guard object. @@ -280,7 +285,8 @@ pub struct PoolGuard<'a, MemProvider: PoolProvider + ?Sized> { deletion_failed_error: Option, } -/// This helper object +/// This helper object can be used to safely access pool data without worrying about memory +/// leaks. impl<'a, MemProvider: PoolProvider> PoolGuard<'a, MemProvider> { pub fn new(pool: &'a mut MemProvider, addr: StoreAddr) -> Self { Self { @@ -398,16 +404,21 @@ mod alloc_mod { /// Pool implementation providing sub-pools with fixed size memory blocks. /// - /// This is a simple memory pool implementation which pre-allocates all sub-pools using a given pool - /// configuration. After the pre-allocation, no dynamic memory allocation will be performed - /// during run-time. This makes the implementation suitable for real-time applications and - /// embedded environments. The pool implementation will also track the size of the data stored - /// inside it. + /// This is a simple memory pool implementation which pre-allocates all subpools using a given + /// pool configuration. After the pre-allocation, no dynamic memory allocation will be + /// performed during run-time. This makes the implementation suitable for real-time + /// applications and embedded environments. + /// + /// The subpool bucket sizes only denote the maximum possible data size being stored inside + /// them and the pool implementation will still track the size of the data stored inside it. + /// The implementation will generally determine the best fitting subpool for given data to + /// add. Currently, the pool does not support spilling to larger subpools if the closest + /// fitting subpool is full. This might be added in the future. /// /// Transactions with the [pool][StaticMemoryPool] are done using a generic - /// [address][StoreAddr] type. - /// Adding any data to the pool will yield a store address. Modification and read operations are - /// done using a reference to a store address. Deletion will consume the store address. + /// [address][StoreAddr] type. Adding any data to the pool will yield a store address. + /// Modification and read operations are done using a reference to a store address. Deletion + /// will consume the store address. pub struct StaticMemoryPool { pool_cfg: StaticPoolConfig, pool: Vec>,