Closed japaric closed 7 years ago
Crazy idea: async iprintln!
. It would no longer be a macro though. Or rather it would a macro
that returns a future ... :confounded:
blink
I'm going to need to brush up on Futures and get back to you!
Something else to think about: error handling. The real Future
trait returns Result
from the poll
method. We can test that here by having e.g. Bytes
return an Err
or when the RX buffer is overrun.
And Timer
can use !
(the bottom type) as its error type.
cc @jamesmunns This may also interest you. A futures-based nonblocking API for timers and serial (UART). BTW, do you know if the teensy C library has a nonblocking API that can I can look at for inspiration?
Everything has been updated to use futures: Timers, Serial, I2C, SPI and the sensors.
cc @istankovic There's a "session types" based I2C "concrete" (no traits) API. See the i2c module. It ended looking quite different from what I sketched before; the main reason is that, at least on the stm32f303 microcontroller, (a) along START you have to send the slave address and wheter you'll be reading/writing bytes across the wire and (b) you have to declare how many bytes you are going to read/write before you send START. The implication of all this is that you can't have and call a start(address)
method (that changes the session type) and then decide whether you are going to read/write an arbitrary number of bytes to that device; it's just not possible. Instead the read
/write
methods must specify which slave they are going to deal with.
Do you have a plan to get Future::wait to not spin tightly?
Definitely interesting! I'll try and look more in depth ASAP.
@thejpster I haven't had my morning coffee yet, but basically the choices for wait would be:
WFI
waiting for an interrupt or callback to trigger a volatile flag marking "ready for processing"yields
the flowyield
, kind of (state is preserved through static
variables).Probably the best implementation of 1
is a WFI
that allows for low power modes, etc. There would be some kind of way to "register" or "check" if you have multiple pending tasks. Options 2
or 3
would require some kind of multithreading, and would more closely match how threading
works on a desktop platform
@thejpster
Do you have a plan to get Future::wait to not spin tightly?
I don't intent to change Future::wait behavior as it's the simplest and cheapest (code size wise) way to transform async functions into blocking functions, which make sense to use in e.g. initialization code. (I might change its name to busy_wait though)
To avoid busy waiting at all in the main loop, I'm going to look into WFI to sleep when there's nothing to do. I'm hoping that the final API usage will look like this:
loop {
let mut progress = false;
// Try to advance tasks
progress |= task1.advance(); // returns false if no progress was made
progress |= task2.advance();
(..)
// If no task progressed
if !progress {
// call WFI and wait until an interrupt unblocks progress on some task
sleep();
}
}
I might have to change Async
to look like this to make this work though:
enum Async<T> {
/// Done
Ready(T),
/// Made some progress
Progressed,
Blocked,
}
:umbrella: The latest upstream changes (presumably ccba1d3) made this pull request unmergeable. Please resolve the merge conflicts.
@japaric I really like this:
loop {
let mut progress = false;
// Try to advance tasks
progress |= task1.advance(); // returns false if no progress was made
progress |= task2.advance();
(..)
// If no task progressed
if !progress {
// call WFI and wait until an interrupt unblocks progress on some task
sleep();
}
}
Update
Everything has been updated to use futures: Timers, Serial, I2C, SPI and the sensors.
Documentation
this deprecates the delay module as the new Timer API can easily replicate the delay::ms functionality.
I want to try a few more complicated peripherals like I2C before deciding whether this makes sense or not. Probably will do an async version of the API of one the sensors as well.
cc @thejpster This may interest you. You can start by looking at the examples.