Skip to content
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

New Proc-macro thread declaration #67

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft

New Proc-macro thread declaration #67

wants to merge 20 commits into from

Conversation

d3zd3z
Copy link
Collaborator

@d3zd3z d3zd3z commented Mar 7, 2025

Create a new proc-macro-based way of declaring threads in Zephyr. Instead of the weird kobj_define macro, provide a zephyr::thread macro that can decorate an ordinary Rust function to become a builder for a ReadyThread that can just be started. Threads can also now be reused after they exit.

This allows something like:

#[zephyr::thread(stack_size = 2048)]
fn mythread(name: &'static str, index: usize) {
    // Body
}

and somewhere in the code, you just call it, which returns a handle that can be used to start the thread.

let joiner = mythread("thename", 42).start();
...
joiner.join();  // Wait for thread to exit, if desired.

There is still some things to clean up, hence the draft, but I wanted to get some exposure.

d3zd3z added 20 commits March 18, 2025 13:02
Implement a proc macro that allows a declaration like:

```rust
fn mythread(arg: usize, arg2: &'static Thing) { .. }
```

With this change, creation of threads, with arbitrary "Send" arguments
can be done without needing allocation.  The macros reserves a static
area alongside the `k_thread` structure to use for handing the threads
over.

This creates a function `mythread` with the given args that returns a
ReadyThread.  This has a `start()` method which will begin execution of
the thread.  This results in a RunningThread, which has a join method
that can be used to wait for termination.

Signed-off-by: David Brown <[email protected]>
Move away from the `kobj_define` task declaration to use the new
`#[zephyr::thread]` to define these.  This allows for a more natural
declaration where the thread just looks like an attribute added to a
regular function declaration.

This also eliminates the static Mutex, as the Mutex now has a
constructor that avoids allocation (it is still put in an Arc, though).

Signed-off-by: David Brown <[email protected]>
Change this declaration from a dynamic allocated type that must never be
dropped to a static declaration.  This is in preparation for a macro to
help with the declaration.

Signed-off-by: David Brown <[email protected]>
There is a fairly fundamental incompatibility between Zephyr spin locks
and the Critical Section specification.  Zephyr spin locks do not allow
nesting from within a single spin lock.  The critical section API only
has an `acquire` and `release` entry, and provides no way (such as a
stack frame) to have a unique context for different invocation places.

Unfortunately, this means we cannot use spin locks for critical
sections.

Instead, this change implements critical sections using irq locking.
The implementation of these macros on Zephyr does try to make them SMP
safe, with a simple atomic lock, but this hasn't yet been tested, so
there is a compile-time assertion against SMP.

Also, these entries cannot be called from user mode.  There are various
other reasons we don't support usermode, so at this time, just have a
compile time assertion that usermode is not enabled in the build.  If it
is needed, we will have to come up with another way to implement this.

Signed-off-by: David Brown <[email protected]>
To help with debugging, try to give created Zephyr threads a readable
name.  Currently, this is based off of the name of the function used in
the declaration of the thread.

Signed-off-by: David Brown <[email protected]>
These are macros in Zephyr, so write explicit wrappers for them, that
bindgen will be able to directly use.

Signed-off-by: David Brown <[email protected]>
Ensure the atomic is set before waking the other thread to prevent a
race where the other thread is able to run through the queue, but not
sleep before we have a chance to wake it.

Signed-off-by: David Brown <[email protected]>
Remove the sample that depended on the old non-standard executor built
around work queues, and use the zephyr-executor.

Signed-off-by: David Brown <[email protected]>
Add this to the device spec.  This allows the `myra_sip_baseboard` to
run Rust code.

Signed-off-by: David Brown <[email protected]>
The pin-weak provides a weak pointer that is compatible with `Pin<Arc>`.
Unfortunately, these are not compatible with the Arc that we use from
portable-atomic-utils.  Provide a simplified implementation of this
until the pin-weak is able to gain this functionality.

Signed-off-by: David Brown <[email protected]>
This reverts commit 9714921.

The SMP errors were being caused by our critical section implementation
incorrectly using spinlocks.

Signed-off-by: David Brown <[email protected]>
After some analysis and testing, remove the compile-failure check for
CONFIG_SMP.

An earlier commit replaced the spinlock-based critical section code with
one that uses `irq_lock()/unlock()`

Although a bit unclear from both the docs and the name of these macros,
the implementation on SMP definitely does have a spinlock to coordinate
between CPUs on SMP.  This doesn't work on some unusual platforms, such
as the rp2040, but those platforms won't work with the rest of Zephyr
yet for the same reason.  Presumably adding support for SMP on this
target will also require fixing irq_lock/unlock.

The main difference between using irq_lock/unlock is that it supports
nested calls, without needing a per-nest-level variable.  The
critical-section API doesn't provide a place to have this variable, so
this allows us to have a critical-section implementation that should
also work on SMP.

Signed-off-by: David Brown <[email protected]>
Change this to be more general about the async executor used, instead of
the zephyr-specific workq-based one.

This doesn't change the sample itself, just the name.

Signed-off-by: David Brown <[email protected]>
The work-queue-based executor was an attempt to implement a Rust
executor that worked with Zephyr work queues.  Although it was possible
to make something that kind of worked, and did have the advantage of
being able to use a few Zephyr primitives, it suffers from several
problems:

- It isn't very efficient, basically combining the overhead of async
  scheduling, triggered work, and the Zephyr thread scheduler.
- It plays poorly with any other use of async.  Since it is useful to be
  able to use external crates for async utilities, this normally just
  results in undefined-behavior.

Now that we have another executor (built from Embassy), this code is
also mostly unneeded.

Signed-off-by: David Brown <[email protected]>
Convert away from the Zephyr-specific async to standard Rust async.
This uses the Embassy crates for time and synchronization.  All tasks
are spawned within a single executor that runs in the main thread.

Signed-off-by: David Brown <[email protected]>
Import the embassy crates, but not by path.  Also, remove the specific
time instance.  Although and specific application will likely want to
use a non-default tick rate, leaving at the 1us tick will allow the time
to be used (with a slight performance cost) with any time base.

Signed-off-by: David Brown <[email protected]>
Import the embassy crates, but not by path.  Also remove the specific
time instance.

Signed-off-by: David Brown <[email protected]>
Don't require that the two time bases be the same.  This allows
applications to work using the default embassy-time base of 1Mhz.  There
is a performance cost to the conversion (which depends on the exact
ratios).  If the time bases are the same (which would be common for an
application built for a single target), then no conversion is needed.

Signed-off-by: David Brown <[email protected]>
Instead of a Struct that has to be carefully used, and requires alloc,
create a macro to statically declare work queues.

Signed-off-by: David Brown <[email protected]>
Update formatting from `cargo fmt`.

Signed-off-by: David Brown <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant