mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
examples: Add asynchronous Rust example
This commit is contained in:
parent
b802a19059
commit
7a348aebde
BIN
examples/rust-async/Cargo.lock
generated
Normal file
BIN
examples/rust-async/Cargo.lock
generated
Normal file
Binary file not shown.
28
examples/rust-async/Cargo.toml
Normal file
28
examples/rust-async/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
name = "rust-async"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Christian Amsüss <chrysn@fsfe.org>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
# Setting the panic mode has little effect on the built code (as Rust on RIOT
|
||||||
|
# supports no unwinding), but setting it allows builds on native without using
|
||||||
|
# the nightly-only lang_items feature.
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
riot-wrappers = { version = "0.9", features = [ "set_panic_handler", "provide_critical_section_1_0", "panic_handler_format" ] }
|
||||||
|
|
||||||
|
embassy-executor-riot = { git = "https://gitlab.com/etonomy/riot-module-examples" }
|
||||||
|
embassy-executor = "0.5"
|
||||||
|
embassy-futures = "0.1.1"
|
||||||
|
static_cell = "2.0.0"
|
||||||
|
|
||||||
|
# While currently this example does not use any RIOT modules implemented in
|
||||||
|
# Rust, that may change; it is best practice for any RIOT application that has
|
||||||
|
# its own top-level Rust crate to include rust_riotmodules from inside
|
||||||
|
# RIOTBASE.
|
||||||
|
rust_riotmodules = { path = "../../sys/rust_riotmodules/" }
|
30
examples/rust-async/Makefile
Normal file
30
examples/rust-async/Makefile
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# name of your application
|
||||||
|
APPLICATION = rust-async
|
||||||
|
|
||||||
|
# If no BOARD is found in the environment, use this default:
|
||||||
|
BOARD ?= native
|
||||||
|
|
||||||
|
# This has to be the absolute path to the RIOT base directory:
|
||||||
|
RIOTBASE ?= $(CURDIR)/../..
|
||||||
|
|
||||||
|
# Some timers for the example
|
||||||
|
USEMODULE += ztimer_msec
|
||||||
|
|
||||||
|
# Required by the async executor
|
||||||
|
USEMODULE += core_thread_flags
|
||||||
|
|
||||||
|
# Comment this out to disable code in RIOT that does safety checking
|
||||||
|
# which is not needed in a production environment but helps in the
|
||||||
|
# development process:
|
||||||
|
DEVELHELP ?= 1
|
||||||
|
|
||||||
|
# Change this to 0 show compiler invocation lines by default:
|
||||||
|
QUIET ?= 1
|
||||||
|
|
||||||
|
# The name of crate (as per Cargo.toml package name, but with '-' replaced with '_')
|
||||||
|
APPLICATION_RUST_MODULE = rust_async
|
||||||
|
BASELIBS += $(APPLICATION_RUST_MODULE).module
|
||||||
|
|
||||||
|
FEATURES_REQUIRED += rust_target
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.include
|
21
examples/rust-async/README.md
Normal file
21
examples/rust-async/README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Rust: Asynchronous programming
|
||||||
|
==============================
|
||||||
|
|
||||||
|
This is an example of how asynchronous Rust applications can be written on RIOT OS.
|
||||||
|
|
||||||
|
The application starts an [embassy] based executor,
|
||||||
|
and then spawn several tasks on the main thread.
|
||||||
|
Unlike RIOT OS threads, these tasks can not preempt each other;
|
||||||
|
they relinquish control of the main thread when performing an asynchronous operation that would block,
|
||||||
|
and are paused by anything that preempts the main thread.
|
||||||
|
|
||||||
|
Behind the scenes, the Rust compiler turns the asynchronous methods into state machines,
|
||||||
|
and the executor polls the state machines to make progress whenever an event wakes them up.
|
||||||
|
|
||||||
|
Upsides of this style of programming are that there is less need for synchronization
|
||||||
|
(for individual tasks or their parts can rely on the work they do synchronously not to be executed concurrently with other tasks if so configured),
|
||||||
|
and that they can share a single stack space.
|
||||||
|
State that is persisted across await points is not stored on the stack,
|
||||||
|
but in adequately sized static memory for each task that can run.
|
||||||
|
|
||||||
|
[embassy]: https://embassy.dev/
|
53
examples/rust-async/src/lib.rs
Normal file
53
examples/rust-async/src/lib.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (C) 2020 Christian Amsüss
|
||||||
|
//
|
||||||
|
// This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
// General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
// directory for more details.
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use riot_wrappers::riot_main;
|
||||||
|
use riot_wrappers::println;
|
||||||
|
use riot_wrappers::ztimer::{Clock, Ticks};
|
||||||
|
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
extern crate rust_riotmodules;
|
||||||
|
|
||||||
|
riot_main!(main);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Create an executor for running async functions
|
||||||
|
static EXECUTOR: StaticCell<embassy_executor_riot::Executor> = StaticCell::new();
|
||||||
|
let executor: &'static mut _ = EXECUTOR.init(embassy_executor_riot::Executor::new());
|
||||||
|
executor.run(|spawner| {
|
||||||
|
spawner.spawn(amain(spawner)).expect("Starting task for the first time");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn amain(spawner: embassy_executor::Spawner) {
|
||||||
|
println!("Running asynchronously:");
|
||||||
|
|
||||||
|
spawner.spawn(fast_messages("A")).expect("Starting task for the first time");
|
||||||
|
spawner.spawn(fast_messages("B")).expect("Task is configured to allow two instances");
|
||||||
|
|
||||||
|
let msec = Clock::msec();
|
||||||
|
|
||||||
|
msec.sleep_async(Ticks(2500)).await;
|
||||||
|
|
||||||
|
println!("Slept 2.5 seconds in 'amain' task, that should be enough.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task(pool_size = 2)]
|
||||||
|
async fn fast_messages(taskname: &'static str) {
|
||||||
|
println!("Task {taskname} is running:");
|
||||||
|
|
||||||
|
let msec = Clock::msec();
|
||||||
|
|
||||||
|
msec.sleep_async(Ticks(1000)).await;
|
||||||
|
println!("Task {taskname} ticked");
|
||||||
|
msec.sleep_async(Ticks(1000)).await;
|
||||||
|
println!("Task {taskname} tocked");
|
||||||
|
msec.sleep_async(Ticks(1000)).await;
|
||||||
|
println!("Task {taskname} is done.");
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user