blob: daa693c205366fcbb784b6d550334322b32e0b2d [file] [edit]
.. _module-pw_kernel-guides:
======
Guides
======
.. pigweed-module-subpage::
:name: pw_kernel
.. note::
This is an early draft. The content may change significantly over the
next few months.
This section provides guides for specific features and tools within ``pw_kernel``.
.. _module-pw_kernel-guides-unit-testing:
------------
Unit testing
------------
``pw_kernel`` includes a lightweight unit testing framework designed for both
bare-metal and kernel-aware tests. This framework is crucial for ensuring the
correctness and reliability of kernel primitives and application code.
Writing a test
==============
Tests are written as standard Rust functions annotated with the ``#[test]``
attribute from the ``unittest`` crate.
The test function should return a ``unittest::Result<()>``.
Here's a simple example of a bare-metal test:
.. code-block:: rust
// in your_module/lib.rs or your_module/tests.rs
#[cfg(test)]
mod tests {
use unittest::test;
fn add(a: u32, b: u32) -> u32 {
a + b
}
#[test]
fn test_addition() -> unittest::Result<()> {
unittest::assert_eq!(add(2, 2), 4);
unittest::assert_ne!(add(2, 2), 5);
unittest::assert_true!(add(1,1) == 2);
Ok(())
}
}
Assertions
==========
The ``unittest`` crate provides several assertion macros:
- ``unittest::assert_eq!(a, b)``: Asserts that ``a`` is equal to ``b``.
- ``unittest::assert_ne!(a, b)``: Asserts that ``a`` is not equal to ``b``.
- ``unittest::assert_true!(expr)``: Asserts that ``expr`` evaluates to true.
- ``unittest::assert_false!(expr)``: Asserts that ``expr`` evaluates to false.
Kernel-aware tests
==================
For tests that require kernel services (e.g., testing scheduler behavior, mutexes,
or timers), you can mark them as needing the kernel by passing the
``needs_kernel`` argument to the ``#[test]`` attribute:
.. code-block:: rust
use kernel::sync::mutex::Mutex;
use unittest::test;
static MY_MUTEX: Mutex<u32> = Mutex::new(0);
#[test(needs_kernel)]
fn test_mutex_locking() -> unittest::Result<()> {
let guard = MY_MUTEX.lock();
unittest::assert_eq!(*guard, 0);
// guard is dropped here, unlocking the mutex
Ok(())
}
Running tests
=============
Tests are typically run via Bazel. See :ref:`module-pw_kernel-quickstart-test`.
The test runner executes all discovered tests and reports their status.
Bare-metal tests are run first, followed by kernel-aware tests if the kernel
is initialized.
.. _module-pw_kernel-guides-panic-detector:
--------------
Panic detector
--------------
``pw_kernel`` includes a tool called ``panic detector`` to statically
analyze a compiled Rust binary (ELF file) to identify all potential panic
locations. This is crucial for ensuring reliability and can significantly
reduce code size by eliminating panic-handling overhead.
For detailed instructions on how to integrate and use this tool, see
:ref:`module-pw_kernel-tooling-panic-detector`.
.. _module-pw_kernel-guides-intrusive-lists:
----------------------
Intrusive linked lists
----------------------
``pw_kernel`` provides a highly efficient and safe intrusive linked list
implementation in :cs:`pw_kernel/lib/list`. This is a fundamental data structure
used throughout the kernel, particularly in the scheduler for managing threads
in run queues and wait queues.
Example usage
=============
.. code-block:: rust
use kernel::lib::list::{self, Link, ForeignList, ForeignBox};
use core::ptr::NonNull;
// 1. Define your struct with an embedded `Link`.
struct MyListItem {
data: u32,
list_link: Link, // The intrusive link
}
// 2. Define an adapter using the `define_adapter!` macro.
// This connects `MyListItem` and its `list_link` field to the list logic.
list::define_adapter!(MyListItemAdapter => MyListItem.list_link);
fn main_example() {
// Create a list that can hold `MyListItem`s.
let mut my_list = ForeignList::<MyListItem, MyListItemAdapter>::new();
// Create some items. In a real scenario, these might be ForeignBox::new_from_ptr
// from statically allocated buffers or a dedicated allocator.
// For simplicity, we'll imagine they are correctly managed ForeignBox instances.
// Note: ForeignBox requires that the underlying memory is valid for its
// entire lifetime and that it is not deallocated by other means.
let mut item1_storage = MyListItem { data: 10, list_link: Link::new() };
let item1 = unsafe { ForeignBox::new_from_ptr(&mut item1_storage) };
let mut item2_storage = MyListItem { data: 20, list_link: Link::new() };
let item2 = unsafe { ForeignBox::new_from_ptr(&mut item2_storage) };
// Add items to the list.
my_list.push_back(item1);
my_list.push_front(item2); // item2 is now at the head.
// Iterate and access items (ForeignList provides safe iteration).
my_list.for_each(|item| {
// pw_log::info!("Item data: {}", item.data);
Ok::<(), ()>(()) // Placeholder Ok for the closure
}).unwrap();
// Pop an item.
if let Some(popped_item) = my_list.pop_head() {
// pw_log::info!("Popped item data: {}", popped_item.data);
// IMPORTANT: The popped_item (a ForeignBox) must be consumed
// or it will panic on drop, ensuring ownership is handled.
popped_item.consume();
}
// Clean up remaining items
while let Some(item) = my_list.pop_head() {
item.consume();
}
}
Considerations
--------------
- **ForeignBox**: The ``ForeignBox`` type is a smart pointer that ensures the
underlying memory is valid for its entire lifetime. It is crucial to call
``consume()`` on a ``ForeignBox`` when it is no longer needed to prevent panics
on drop.
- ``UnsafeList`` **is unsafe**: Requires careful handling to prevent dangling
pointers or double-frees if not using ``ForeignList``.
- **Single List Membership**: An item can only be part of one list at a time
using a single ``Link`` member.
The intrusive list is a powerful tool for performance-critical data structures
within the kernel. You'll see it used in :cs:`pw_kernel/kernel/scheduler.rs` for
managing thread queues.