Stateful embedded library in Rust - rust

I want to develop a library in Rust for microcontrollers that holds some state information.
This state data cannot be transferred to the caller of the library. I am using #![no_std]. The library should be suitable for bare metal applications and RTOS like Zephyr OS or FreeRTOS.
My approach so far was to use lazy_static:
use heapless::Vec;
lazy_static! {
static ref nodes: Vec<u8, 100> = Vec::new();
}
fn foo(){
nodes.push(1);
}
This example doesn't compile. I am getting the following error:
cannot borrow data in dereference of `nodes` as mutable
trait `DerefMut` is required to modify through a dereference, but it is not implemented for `nodes`
Googling this error I fond out that a mutex is needed, see link. I don't know how to achieve this in #![no_std] code.
However more important is the question: What is the idiomatic way of handling global state when #![no_std] is used?

Related

Why does futures::lock::Mutex not implement RefUnwindSafe

futures::lock::Mutex is not implementing RefUnwindSafe trait (see https://docs.rs/futures/0.3.17/futures/lock/struct.Mutex.html#impl-RefUnwindSafe )
Why is not safe to pass a futures::lock::Mutex inside std::panic::catch_unwind?
Reproducible code:
use std::panic::catch_unwind;
use futures::lock::Mutex;
fn main() {
let m = Mutex::new(String::new());
catch_unwind(|| {
m.lock()
});
}
Could futures::lock::Mutex implement the RefUnwindSafe trait ?
The documentation for the UnwindSafe trait hints at a reason for this:
Who implements UnwindSafe?
Types such as &mut T and &RefCell are examples which are not unwind safe. The general idea is that any mutable state which can be shared across catch_unwind is not unwind safe by default. This is because it is very easy to witness a broken invariant outside of catch_unwind as the data is simply accessed as usual.
Types like &Mutex, however, are unwind safe because they implement poisoning by default. They still allow witnessing a broken invariant, but they already provide their own “speed bumps” to do so.
futures::lock::Mutex provides mutability like &mut T and RefCell<T>, but does not implement the poisoning feature of std::sync::Mutex<T>, so it does not implement UnwindSafe.
Though as the documentation points out, the UnwindSafe is less about memory safety and more about upholding logical invariants - hence why neither UnwindSafe nor AssertUnwindSafe are unsafe.

Why is the Write trait implemented for &File and &TcpStream? Is it safe? [duplicate]

This question already has an answer here:
Why is it possible to implement Read on an immutable reference to File?
(1 answer)
Closed 3 years ago.
All methods in the io::Write trait accept &mut self. It makes sense since conceptually it needs to mutate some internal state. For the same reason, it is only natural to see it has a blanket implementation for &mut W if W is io::Write:
impl<'_, W: Write + ?Sized> Write for &'_ mut W
On the other hand, I find it strange that it is also implemented for &File and &TcpStream in addition to File and TcpStream themselves.
This discrepancy bothers me a little bit. Is it done only for convenience? Or is it the nature of I/O make this safe?
Noticed this when trying out crate may, a stackful coroutines library. Its net::TcpStream doesn't have io::Write implemented for &TcpStream. So some code suddenly failed to compile:
use may::{coroutine::scope, go}; // may = "0.3"
use std::{
error::Error,
io::Write,
net::TcpStream,
};
fn send_to_peer(msg: &str, peer: &TcpStream) -> Result<(), Box<dyn Error>>
{
scope(|scope| {
go!(scope, move || {
let mut socket = peer;
let _ = socket.write_all(msg.as_bytes());
});
});
Ok(())
}
This compiles. Switch to may::net::TcpStream and it will fail.
Wasn't intended to answer my own question, but #SvenMarnach's comment prompted me to check out the POSIX standard with respect to API thread safety. It addresses my core concern.
Specifically, the section 2.9.1 of its 2018 edition reads:
All functions defined by this volume of POSIX.1-2017 shall be thread-safe, except that the following functions need not be thread-safe.
And write is not among them. Furthermore, section 2.9.7 dictates:
All of the following functions shall be atomic with respect to each other in the effects specified in POSIX.1-2017 when they operate on regular files or symbolic links
And write should be atomic to other listed APIs.
Because of this thread-safety at operating system level, the Rust language level constructions, namely a value, its (shared) reference and its mutable (exclusive) reference get subsumed; they are all semantically thread-safe here.
The implementation of io::Write for &File and &TcpStream then serves as an optimization. Otherwise, one would need a Mutex to write to them from multiple threads, which introduces unnecessary overhead.

Access a singleton across multiple modules [duplicate]

This question already has an answer here:
Rust Can't import Singleton From global space into another module in another file
(1 answer)
Closed 4 years ago.
I am trying to use the lazy_static crate to create a singleton and use it in a different module. Is that possible or even recommended? I'm still learning how Rust programs should be structured and have been making every file its own module.
I have the following in main.rs and I can access its values
lazy_static! {
static ref GAMEDATA: gamedata::data::GameDataS =
gamedata::data::load_data("./src/assets/data.json".to_string());
}
fn main() {
println!("data{}", GAMEDATA.width);
}
When trying to access GAMEDATA in a different module, I get
not found in this scope
for example in a module called game
pub struct Game {}
impl Game {
println!("data{}", GAMEDATA.width);
}
Is it possible to make a global variable across all modules? Is there some other way I should be thinking about it? Perhaps not using modules as often?
If your static variable is in another non-parent module, your problem seems to be a missing pub modifier before static. Also, as others pointed out, the code in which you use the variable (the impl block) is not valid Rust syntax.
Besides that, you will need to import the static variable with use (E.g. use GAMEDATA;), see Quan Brew's answer.
However, I want to discuss about the use of static and singleton pattern in Rust.
Static variables in Rust
In Rust we generally avoid static variables. In most cases they could be replaced with a proper constant via const. Because static variables may be shared between threads, having them with external mutability is unsafe in Rust. This is why you cannot have external mutability with lazy_static.
Although statics with external mutability do have their uses, they are kind of specific, should be justified and avoided otherwise. Interior mutability, as described in this section of the Rust Book is not even allowed to be shared among threads.
Singleton Pattern in Rust
I don't think it is a good idea to use static to have singleton pattern. This pattern is not common in Rust. We generally pass all mutable things as arguments.
Solutions if you need immutable data
Make it a constant with const.
If there is too much data, use static.
If you need non-constant initialization, you can keep lazy_static.
Solutions if you need to mutate data
Put your singleton into a Mutex or another lock. This will ensure correct concurrent accesses.
Make it thread local with thread_local macro + inner mutability with RefCell
Give up the "singleton pattern" idea and pass the structure via arguments (recommended).
You need use to import GAMEDATA into the current scope, as described in the modules section in the book.
Example code (playground):
#[macro_use]
extern crate lazy_static; // 1.1.0
lazy_static! {
static ref GAMEDATA: String = "hello".to_string();
}
mod foo {
use GAMEDATA;
pub fn bar() {
println!("{}", *GAMEDATA);
}
}
fn main() {
foo::bar();
}
However, the singleton pattern is not recommended in Rust. For beginners in the learning phase, you'd best avoid singletons. (see bzim's answer)

Rust compiler support for interrupts

I'm writing a bare-metal application for an AMR board in Rust that involves interrupt service routines. Currently, I use #naked functions with my own assembler prolog/epilog. However, I wonder whether there is a better (and hopefully more portable) way I've missed, maybe an #interrupt-like attribute in Rust nightly or any other compiler support. I think along the lines of GCC's __attribute__ ((interrupt ("IRQ"))), since Rust's backend LLVM provides such an attribute.
Interrupts are simply another type of calling convention. For the AVR port of Rust, we added two new types of calling convention, one for each kind of interrupt that AVR supports.
The authoritative list of calling conventions is the source code. Rust 1.16 lists these:
#[derive(PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Clone, Copy, Debug)]
pub enum Abi {
// NB: This ordering MUST match the AbiDatas array below.
// (This is ensured by the test indices_are_correct().)
// Single platform ABIs
Cdecl,
Stdcall,
Fastcall,
Vectorcall,
Aapcs,
Win64,
SysV64,
PtxKernel,
Msp430Interrupt,
// Multiplatform / generic ABIs
Rust,
C,
System,
RustIntrinsic,
RustCall,
PlatformIntrinsic,
Unadjusted
}
The unstable book also mentions that the different calling conventions exist.
To use these, you'd declare your function with it:
#![feature(abi_msp430_interrupt)]
extern "msp430-interrupt" fn handler() {}
It's still up to you to register the function as an exception handler with the interrupt vector table (or equivalent).
Of course, you may need to submit a PR that informs the Rust frontend of the specific LLVM calling convention to use, if yours isn't already in this list.
copy the information here, shamelessly ;)
https://github.com/nix-rust/nix
https://users.rust-lang.org/t/unix-signals-in-rust/733/3
use nix::sys::signal;
extern fn handle_sigint(_:i32) {
println!("Interrupted!");
panic!();
}
fn main() {
let sig_action = signal::SigAction::new(handle_sigint,
signal::SockFlag::empty(),
signal::SigSet::empty());
signal::sigaction(signal::SIGINT, &sig_action);
}

Why does AtomicUsize not implement Send?

std::sync::atomic::AtomicUsize implements Sync which means immutable references are free of data races when shared between multiple threads. Why does AtomicUsize not implement Send? Is there state which is linked to the thread that created the atomic or is this a language design decision relating to the way atomics are intended to be used i.e. via a Arc<_> etc.
It's a trick! AtomicUsize does implement Send:
use std::sync::atomic::AtomicUsize;
fn checker<T>(_: T) where T: Send {}
fn main() {
checker(AtomicUsize::default());
}
In fact, there's even an automated test that ensures this is the case.
Rust 1.26
These auto traits are now documented, thanks to a change made to rustdoc.
Previous versions
The gotcha lies in how Send is implemented:
This trait is automatically derived when the compiler determines it's appropriate.
This means that Rustdoc doesn't know that Send is implemented for a type because most types don't implement it explicitly.
This explains why AtomicPtr<T> shows up in the implementers list: it has a special implementation that ignores the type of T.

Resources