diff --git a/CMakeLists.txt b/CMakeLists.txt index e4debcc..3d4f417 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 @@ -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 diff --git a/zephyr/src/embassy.rs b/zephyr/src/embassy.rs index 8e29c94..8bbc254 100644 --- a/zephyr/src/embassy.rs +++ b/zephyr/src/embassy.rs @@ -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 @@ -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")] diff --git a/zephyr/src/lib.rs b/zephyr/src/lib.rs index 79acab3..cccdfaf 100644 --- a/zephyr/src/lib.rs +++ b/zephyr/src/lib.rs @@ -43,13 +43,6 @@ //! [`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. //! @@ -57,7 +50,6 @@ //! [`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 diff --git a/zephyr/src/object.rs b/zephyr/src/object.rs index 3a1fe25..fb548f1 100644 --- a/zephyr/src/object.rs +++ b/zephyr/src/object.rs @@ -231,7 +231,7 @@ pub trait ObjectInit { /// 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); diff --git a/zephyr/src/sync.rs b/zephyr/src/sync.rs index 8b8820a..001ea8b 100644 --- a/zephyr/src/sync.rs +++ b/zephyr/src/sync.rs @@ -33,7 +33,7 @@ mod pinweak { /// Safe Pinned Weak references. /// - /// Pin> can't be converted to/from Weak safely, because there is know way to know if a given + /// `Pin>` 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. /// diff --git a/zephyr/src/work.rs b/zephyr/src/work.rs index 2da6786..c3ed8d2 100644 --- a/zephyr/src/work.rs +++ b/zephyr/src/work.rs @@ -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 @@ -27,84 +29,13 @@ //! 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: //! @@ -112,17 +43,11 @@ //! 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 //! @@ -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