I’d much rather have them exposed as some form of atomic that is restricted to operations that are atomic on this specific hardware.
I’d much rather have them exposed as some form of atomic that is restricted to operations that are atomic on this specific hardware.
Good HAL crates don't have the entire register set as one struct, thank goodness. And for something like printing a message to serial in a panic handler, there are still unsafe options to yank control of registers, same as C.
What I really like about some of the HAL crates is the usage of builder patterns while configuring a peripheral. Setting up timer parameters before starting it by design is chef’s kiss wonderful.
But if you want to put the whole peripheral set in a static global, you can do that with a RefCell<Mutex<T>> or something similar. Or just yolo it and use unsafe blocks to ditch the mutex.
Yes, it's all trade-offs. You aren't going to avoid all shared responsibility, and will always need micro-coordination. But safe Rust forces you to assign ownership to every structure, at every scale. Some similar effects to opaque data structures, or actors. But unlike those techniques, it allows you to dynamically transfer responsibility.
The most obvious issue is use-after-"free", use-before-"allocate". "Free" and "allocate" don't just refer to malloc--it's any situation where you pinky-swear you aren't going to be touching something.
But more problematic is this kind of code is easy, even natural, to write in C:
- Task A is waiting for a state changes on resources Q1 and Q2. When it sees a particular set, it will modify resource Q3's state.
- Task B is firing off events to Q1 and handling error responses. It might need to stop Q1.
- Since Q1 is no longer changing state, what happens to Q3? Who's responsible for keeping stuff straight? What's "stuff"? Do we need to worry about Q2?
Rust is going to very strongly push you to explicitly designate what owns and is responsible for Q1, Q2, Q3, at all times. "B's got Q1, so A can't even look at it. I'm @#!* going to have to make A tell B what it wants and let B handle it." That was painful, but a good thing.
* Note to self: Write a blog post titled "Atomics won't save you now!" Start a band named "Useless Atomics".
You could also write wrappers, for example one that automatically turns off interrupt when you do a write operation or gives you a guard that lets you write and read the register, which turns off interrupts until the guard is dropped. Or one that has a lock or uses atomics. Plenty of options you can use here.