zephyr::object

Struct ZephyrObject

Source
pub struct ZephyrObject<T> { /* private fields */ }
Expand description

§Init/move safe objects

In Rust code, many language features are designed around Rust’s “move semantics”. Because of the borrow checker, the Rust compiler has full knowledge of when it is safe to move an object in memory, as it will know that there are no references to it.

However, most kernel objects in Zephyr contain self-referential pointers to those objects. The traditional way to handle this in Rust is to use Pin. However, besides Pin being awkward to use, it is generally assumed that the underlying objects will be dynamically allocated. It is desirable to allow as much functionality of Zephyr to be used without explicitly requiring alloc.

The original solution (Wrapped), used a type Fixed that either referenced a static, or contained a Pin<Box<T>> of the Zephyr kernel object. This introduces overhead for both the enum as well as the actual reference itself.

Some simple benchmarking has determined that it is just as efficient, or even slightly more so, to represent each object instead as a UnsafeCell contaning the Zephyr object, and an atomic pointer that can be used to determine the state of the object.

This type is not intended for general use, but for the implementation of wrappers for Zephyr types that require non-move semantics.

§Safety

The Zephyr APIs require that once objects have been initialized, they are not moved in memory. To avoid the inconvenience of managing ‘Pin’ for most of these, we rely on a run-time detection both of initialization and non-movement. It is fairly easy, as a user of an object in Rust to avoid moving it. Generally, in an embedded system, objects will either live on the stack of a single persistent thread, or will be statically allocated. Both of these cases will result in objects that don’t move. However, we want initialization to be able to happen on first use not when the constructor runs, because the semantics of most constructors invovles a move (even if that is often optimized away).

Note that this does not solve the case of objects that must not be moved even after the object has a single Rust reference (threads, and work queues, notably, or timers with active callbacks).

To do this, each object is paired with an Atomic pointer. The pointer can exist in three state:

  • null: The object has not been initialized. It is safe to move the object at this time.
  • pointer that equals the addres of the object itself. Object has been initialized, and can be used. It must not be moved.
  • pointer that doesn’t match the object. This indicates that the object was moved, and is invalid. We treat this as a panic condition.

Implementations§

Source§

impl<T> ZephyrObject<T>
where ZephyrObject<T>: ObjectInit<T>,

Source

pub const fn new_raw() -> Self

Construct a new Zephyr Object.

The ‘init’ function will be given a reference to the object. For objects that have initialization parameters (specifically Semaphores), this can be used to hold those parameters until the real init is called upon first use.

The ‘setup’ function must not assume the address given to it will persist. The object can be freely moved by Rust until the ‘init’ call has been called, which happens on first use.

Source

pub const fn get_uninit(&self) -> *mut T

Get a reference, without initializing the item.

This is useful during a const constructor to be able to stash values in the item.

Source

pub unsafe fn get(&self) -> *mut T

Get a reference to the underlying zephyr object, ensuring it has been initialized properly. The method is unsafe, because the caller must ensure that the lifetime of &self is long enough for the use of the raw pointer.

§Safety

If the object has not been initialized, It’s ‘init’ method will be called. If the object has been moved since init was called, this will panic. Otherwise, the caller must ensure that the use of the returned pointer does not outlive the &self.

The ‘init’ method will be called within a critical section, so should be careful to not block, or take extra time.

Trait Implementations§

Auto Trait Implementations§

§

impl<T> !Freeze for ZephyrObject<T>

§

impl<T> !RefUnwindSafe for ZephyrObject<T>

§

impl<T> Send for ZephyrObject<T>
where T: Send,

§

impl<T> !Sync for ZephyrObject<T>

§

impl<T> Unpin for ZephyrObject<T>
where T: Unpin,

§

impl<T> UnwindSafe for ZephyrObject<T>
where T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.