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

async API #52

Closed
wants to merge 1 commit into from
Closed

async API #52

wants to merge 1 commit into from

Conversation

japaric
Copy link
Owner

@japaric japaric commented Oct 28, 2016

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.

@japaric
Copy link
Owner Author

japaric commented Oct 28, 2016

Crazy idea: async iprintln!. It would no longer be a macro though. Or rather it would a macro that returns a future ... 😖

@thejpster
Copy link

blink

I'm going to need to brush up on Futures and get back to you!

@japaric
Copy link
Owner Author

japaric commented Oct 28, 2016

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 Error when the RX buffer is overrun.

@japaric
Copy link
Owner Author

japaric commented Oct 28, 2016

And Timer can use ! (the bottom type) as its error type.

@japaric
Copy link
Owner Author

japaric commented Oct 29, 2016

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?

@japaric japaric changed the title async API for timers and USART using futures async API Nov 23, 2016
@japaric
Copy link
Owner Author

japaric commented Nov 23, 2016

Update

Everything has been updated to use futures: Timers, Serial, I2C, SPI and the sensors.

Documentation

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.

@thejpster
Copy link

Do you have a plan to get Future::wait to not spin tightly?

@jamesmunns
Copy link

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:

  1. A tight loop (maybe with delays) or WFI waiting for an interrupt or callback to trigger a volatile flag marking "ready for processing"
  2. A real threaded environment (separated stacks, like what you see in most RTOSs), where the wait yields the flow
  3. A semi-real threaded environment (non separated stacks, like what you see in Contiki), where there is still a yield, 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

@japaric
Copy link
Owner Author

japaric commented Nov 23, 2016

@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,
}

@homunkulus
Copy link
Collaborator

☔ The latest upstream changes (presumably ccba1d3) made this pull request unmergeable. Please resolve the merge conflicts.

@istankovic
Copy link

@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();
    }
}

@japaric
Copy link
Owner Author

japaric commented May 10, 2017

All the API is nonblocking now. You can use the RTFM framework to do multitasking.

@japaric japaric closed this May 10, 2017
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.

5 participants