Skip to content

Fix CI problem with doc generation #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ ${config_paths}
add_custom_target(librustapp ALL
DEPENDS ${DUMMY_FILE}
# The variables, defined at the top level, don't seem to be accessible here.
offsets_h
syscall_list_h_target
driver_validation_h_target
kobj_types_h_target
Expand Down Expand Up @@ -237,6 +238,7 @@ ${config_paths}
add_custom_target(rustdoc
DEPENDS generate_rust_docs
# The variables, defined at the top level, don't seem to be accessible here.
offsets_h
syscall_list_h_target
driver_validation_h_target
kobj_types_h_target
Expand Down
10 changes: 0 additions & 10 deletions zephyr/src/embassy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,6 @@
//! It is perfectly permissible to use the `executor-thread` feature from embassy-executor on
//! Zephyr, within the following guidelines:
//!
//! - The executor is incompatible with the async executor provided within [`crate::kio`], and
//! because there are no features to enable this, this functions will still be accessible. Be
//! careful. You should enable `no-kio` in the zephyr crate to hide these functions.
//! - This executor does not coordinate with the scheduler on Zephyr, but uses an
//! architecture-specific mechanism when there is no work. On Cortex-M, this is the 'wfe'
//! instruction, on riscv32, the 'wfi' instruction. This means that no tasks of lower priority
Expand All @@ -74,13 +71,6 @@
//!
//! ## Caveats
//!
//! The executor provided by Embassy is fundamentally incompatible with the executor provided by
//! this crate's [`crate::kio`] and [`crate::work::futures`]. Trying to use the functionality
//! provided by operations, such as [`Semaphore::take_async`], will generally result in a panic.
//! These routines are conditionally compiled out when `executor-zephyr` is enabled, but there is no
//! way for this crate to detect the use of embassy's `executor-threaded`. Combining these will
//! result in undefined behavior, likely difficult to debug crashes.
//!
//! [`Semaphore::take_async`]: crate::sys::sync::Semaphore::take_async

#[cfg(feature = "time-driver")]
Expand Down
8 changes: 0 additions & 8 deletions zephyr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,13 @@
//! [`work::WorkQueue`] allow creation of Zephyr work queues to be used from Rust. The
//! [`work::Work`] item had an action that will be invoked by the work queue, and can be manually
//! submitted when needed.
//! - [`kio`]: An implementation of an async executor built around triggerable work queues in
//! Zephyr. Although there is a bit more overhead to this executor, it is compatible with many of
//! the Zephyr synchronization types, and many of these [`sys::sync::Semaphore`], and
//! [`sync::channel`] will provide `_async` variants of most of the blocking operations. These
//! will return a `Future`, and can be used from async code started by the [`spawn`] function.
//! In addition, because Zephyr's work queues do not work well with Zephyr's Mutex type, this is
//! also a [`kio::sync::Mutex`] type that works with async.
//! - [`logging`]: A logging backend for Rust on Zephyr. This will log to either `printk` or
//! through Zephyr's logging framework.
//!
//! [`Instant`]: time::Instant
//! [`Duration`]: time::Duration
//! [`std::sync::atomic`]: https://doc.rust-lang.org/std/sync/atomic/
//! [`std::sync::Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
//! [`spawn`]: kio::spawn
//!
//! In addition to the above, the [`kconfig`] and [`devicetree`] provide a reflection of the kconfig
//! settings and device tree that were used for a specific build. As such, the documentation
Expand Down
2 changes: 1 addition & 1 deletion zephyr/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ pub trait ObjectInit<T> {
/// Initialize the object.
///
/// This is called upon first use. The address given may (and generally will) be different than
/// the initial address given to the `setup` call in the [`ZephyrObject::new`] constructor.
/// the initial address given to the `setup` call in the [`ZephyrObject::new_raw`] constructor.
/// After this is called, all subsequent calls to [`ZephyrObject::get`] will return the same
/// address, or panic.
fn init(item: *mut T);
Expand Down
2 changes: 1 addition & 1 deletion zephyr/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ mod pinweak {

/// Safe Pinned Weak references.
///
/// Pin<Arc<T>> can't be converted to/from Weak safely, because there is know way to know if a given
/// `Pin<Arc<T>>` can't be converted to/from Weak safely, because there is know way to know if a given
/// weak reference came from a pinned Arc. This wraps the weak reference in a new type so we know
/// that it came from a pinned Arc.
///
Expand Down
85 changes: 4 additions & 81 deletions zephyr/src/work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
//! having the `k_work` embedded in their structure, and Zephyr schedules the work when the given
//! reason happens.
//!
//! At this time, only the basic work queue type is supported.
//!
//! Zephyr's work queues can be used in different ways:
//!
//! - Work can be scheduled as needed. For example, an IRQ handler can queue a work item to process
Expand All @@ -27,102 +29,25 @@
//! when the work is complete. The work queue scheduling functions are designed, and intended, for
//! a given work item to be able to reschedule itself, and such usage is common.
//!
//! ## Waitable events
//!
//! The triggerable work items can be triggered to wake on a set of any of the following:
//!
//! - A signal. `k_poll_signal` is a type used just for waking work items. This works similar to a
//! binary semaphore, but is lighter weight for use just by this mechanism.
//! - A semaphore. Work can be scheduled to run when a `k_sem` is available. Since
//! [`sys::sync::Semaphore`] is built on top of `k_sem`, the "take" operation for these semaphores
//! can be a trigger source.
//! - A queue/FIFO/LIFO. The queue is used to implement [`sync::channel`] and thus any blocking
//! operation on queues can be a trigger source.
//! - Message Queues, and Pipes. Although not yet provided in Rust, these can also be a source of
//! triggering.
//!
//! It is important to note that the trigger source may not necessarily still be available by the
//! time the work item is actually run. This depends on the design of the system. If there is only
//! a single waiter, then it will still be available (the mechanism does not have false triggers,
//! like CondVar).
//!
//! Also, note, specifically, that Zephyr Mutexes cannot be used as a trigger source. That means
//! that locking a [`sync::Mutex`] shouldn't be use within work items. There is another
//! [`kio::sync::Mutex`], which is a simplified Mutex that is implemented with a Semaphore that can
//! be used from work-queue based code.
//!
//! # Rust `Future`
//!
//! The rust language, also has built-in support for something rather similar to Zephyr work queues.
//! The main user-visible type behind this is [`Future`]. The rust compiler has support for
//! functions, as well as code blocks to be declared as `async`. For this code, instead of directly
//! returning the given data, returns a `Future` that has as its output type the data. What this
//! does is essentially capture what would be stored on the stack to maintain the state of that code
//! into the data of the `Future` itself. For rust code running on a typical OS, a crate such as
//! [Tokio](https://tokio.rs/) provides what is known as an executor, which implements the schedule
//! for these `Futures` as well as provides equivalent primitives for Mutex, Semaphores and channels
//! for this code to use for synchronization.
//!
//! It is notable that the Zephyr implementation of `Future` operations under a fairly simple
//! assumption of how this scheduling will work. Each future is invoked with a Context, which
//! contains a dynamic `Waker` that can be invoked to schedule this Future to run again. This means
//! that the primitives are typically implemented above OS primitives, where each manages wake
//! queues to determine the work that needs to be woken.
//!
//! # Bringing it together.
//!
//! There are a couple of issues that need to be addressed to bring work-queue support to Rust.
//! First is the question of how they will be used. On the one hand, there are users that will
//! definitely want to make use of `async` in rust, and it is important to implement a executor,
//! similar to Tokio, that will schedule this `async` code. On the other hand, it will likely be
//! common for others to want to make more direct use of the work queues themselves. As such, these
//! users will want more direct access to scheduling and triggering of work.
//!
//! ## Future erasure
//!
//! One challenge with using `Future` for work is that the `Future` type intentionally erases the
//! details of scheduling work, reducing it down to a single `Waker`, which similar to a trait, has
//! a `wake` method to cause the executor to schedule this work. Unfortunately, this simple
//! mechanism makes it challenging to take advantage of Zephyr's existing mechanisms to be able to
//! automatically trigger work based on primitives.
//!
//! As such, what we do is have a structure `Work` that contains both a `k_work_poll` as well as
//! `Context` from Rust. Our handler can use a mechanism similar to C's `CONTAINER_OF` macro to
//! recover this outer structure.
//!
//! There is some extra complexity to this process, as the `Future` we are storing associated with
//! the work is `?Sized`, since each particular Future will have a different size. As such, it is
//! not possible to recover the full work type. To work around this, we have a Sized struct at the
//! beginning of this structure, that along with judicious use of `#[repr(C)]` allows us to recover
//! this fixed data. This structure contains the information needed to re-schedule the work, based
//! on what is needed.
//!
//! ## Ownership
//!
//! The remaining challenge with implementing `k_work` for Rust is that of ownership. The model
//! taken here is that the work items are held in a `Box` that is effectively owned by the work
//! itself. When the work item is scheduled to Zephyr, ownership of that box is effectively handed
//! off to C, and then when the work item is called, the Box re-constructed. This repeats until the
//! work is no longer needed (e.g. when a [`Future::poll`] returns `Ready`), at which point the work
//! will be dropped.
//! work is no longer needed, at which point the work will be dropped.
//!
//! There are two common ways the lifecycle of work can be managed in an embedded system:
//!
//! - A set of `Future`'s are allocated once at the start, and these never return a value. Work
//! Futures inside of this (which correspond to `.await` in async code) can have lives and return
//! values, but the main loops will not return values, or be dropped. Embedded Futures will
//! typically not be boxed.
//! - Work will be dynamically created based on system need, with threads using [`kio::spawn`] to
//! create additional work (or creating the `Work` items directly). These can use [`join`] or
//! [`join_async`] to wait for the results.
//!
//! One consequence of the ownership being passed through to C code is that if the work cancellation
//! mechanism is used on a work queue, the work items themselves will be leaked.
//!
//! The Future mechanism in Rust relies on the use of [`Pin`] to ensure that work items are not
//! moved. We have the same requirements here, although currently, the pin is only applied while
//! the future is run, and we do not expose the `Box` that we use, thus preventing moves of the work
//! items.
//! These work items are also `Pin`, to ensure that the work actions are not moved.
//!
//! ## The work queues themselves
//!
Expand Down Expand Up @@ -170,8 +95,6 @@
//! [`sys::sync::Semaphore`]: crate::sys::sync::Semaphore
//! [`sync::channel`]: crate::sync::channel
//! [`sync::Mutex`]: crate::sync::Mutex
//! [`kio::sync::Mutex`]: crate::kio::sync::Mutex
//! [`kio::spawn`]: crate::kio::spawn
//! [`join`]: futures::JoinHandle::join
//! [`join_async`]: futures::JoinHandle::join_async

Expand Down