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

Multitasking models #55

Closed
japaric opened this issue Feb 28, 2018 · 12 comments
Closed

Multitasking models #55

japaric opened this issue Feb 28, 2018 · 12 comments

Comments

@japaric
Copy link
Member

japaric commented Feb 28, 2018

(Originally this was issue #45 but that turned into an RTOS only discussion)

Unlike Linux / Windows / *BSD / etc. targets where the OS provides threading support,
there exist no standard way to do multitasking on embedded targets. This means that we
either have to implement a multitasking mechanism ourselves or bind to a C implementation.
There are few different ways to do multitasking:

Preemptive multitasking with support for task prioritization. Hardware scheduler with zero software
(bookkeeping) overhead. Dead lock free execution plus all the memory safety that Rust grants.

@japaric is working on / maintaining the Cortex-M port.

  • Tokio-like cooperative event loop.

Basically, cooperative multitasking that avoids the "all wakeups must poll all events" problem.

There's an implementation for the STM32F4 here but I haven't look at it in detail. I think
that whatever implementation we end up adopting should meet these requirements:

  • Should integrate with the device sleep functionality.
  • It should be easy to use it with embedded-hal drivers.
  • It should not require dynamic memory allocation, or features that require memory allocation should
    be opt-in.
  • It should be possible to integrate this event loop with preemptive multitasking models like RTFM
    and the thread model.

@japaric is looking into a Cortex-M implementation of this and will write about his findings in a
blog post.

A lot of embedded programmers are used to thread programming so this multitasking model should be
well supported. Here we have the choice to implement threading support in Rust or to bind an
existing RTOS / C implementation.

If we go the Rust route I think Chromium OS Embedded Controller implementation (MIT license)
could be used as a reference.

If we go the C route perhaps we could bind Amazon's FreeRTOS. I think that at some point I saw
some FreeRTOS bindings but that was before Amazon bought it.

Maybe thejpster can recommend some RTOS to bind? See #45

@japaric won't work on this due to conflict of interests (he's already working on RTFM)


Ideally, all these models should have ports for the all the embedded targets. Also, the
embedded-hal drivers should play nicely will all these multitasking models (without
duplicating a bunch of code).

@Nemo157
Copy link

Nemo157 commented Mar 6, 2018

  • Tokio-like cooperative event loop.

[...]

@japaric is looking into a Cortex-M implementation of this and will write about his findings in a
blog post.

Any currently public information on this? I'm currently looking into what is necessary for an ergonomic futures wrapper around the embedded-hal traits along with all the associated machinery to run them, so knowing what else is being worked on in this space would be very useful.

@puzrin
Copy link

puzrin commented Mar 30, 2018

Note about Event Loop. IMHO support of something like async/await required. With pure promises/futures and callbacks it's still not convenient to read and write code. Of cause, async/await helps only with linear logic, but it's the most common case.

@Nemo157
Copy link

Nemo157 commented Mar 30, 2018

@puzrin that's why I'm attempting to build off the futures crate, there are #[async]/await! macros available to work with the futures defined in that crate. Using them is still a bit of a mess at the moment (minimum of 3 nightly features required), but I believe getting those macros supported on stable Rust is one of the goals of the Rust 2018 Edition.

@puzrin
Copy link

puzrin commented Mar 30, 2018

@Nemo157 yes, thanks. I'd like to clarify. There are 3 "components" to keep code clean:

  • event loop
  • async/await (or something of this kind)
  • futures at HAL level

Partial combination is still much better than nothing, but...

@andreytkachenko
Copy link

I have made some prof-of-concept of async/await crate usage on Cortex-M4 board and it looks pretty interesting and promising. https://github.com/andreytkachenko/async-hal-stm32

@bergus
Copy link

bergus commented Jun 23, 2018

@andreytkachenko Is there any documentation or do I need to dig through the code? I'm really excited about a cooperative event loop with await syntax. Where's a good place to start?

@andreytkachenko
Copy link

@bergus, I made it as simple as possible (thats why it is PoC), there are some examples. Once async/await will land in nightly I'll try add some more examples to bobbin-sdk on my stm32f429i.

@bergus
Copy link

bergus commented Jun 29, 2018

@andreytkachenko Thanks, I've skimmed through everything, wasn't that much code after all. Unfortunately none of the examples feature concurrency of multiple tasks (hc_sr04 seems like an attempt, but still only does reactor::execute(task_blink…)).

Is this set_action_flag really necessary? I really dislike the idea of having a global static variable, and every future implementer having to know about it (saying that it will run only in that particular reactor). But I guess that's how futures work in Rust... :-/ (Update: in futures 0.2 there's a Waker that seems to solve this, but requires every interrupt event to store a reference to one waker)

There's a race condition in the reactor code: if an interrupt happens between the last check for while unsafe {ACTION} and the asm::wfi(), it hangs (until the next interrupt).

Also I don't like these timer CHANNELS, USART_VALUEs and EXTI_LINE_FLAGs. It seems cumbersome to have static variables for all these values in RAM while they already exist in the peripheral. All the interrupts do is copy the interrupt flag from the peripheral to ram, clear it in the peripheral and activate the reactor. I think I found a better approach: don't clear the event, but instead just disable the interrupt (not the whole interrupt, which typically handles multiple events, but just the event-triggers-interrupt-request flag - e.g. TIE in timer DIER or TX/RX*IE in usart CR1). Then do the peripheral access in the future's poll function itself, and after you cleared the event there you'd re-enable the interrupt (if you need it again at all).

@andreytkachenko
Copy link

@bergus, yes I thought about updating it for futures 0.2, but I am doing it as one more example for stm32f429-discovery board in bobbin-sdk, you can try add your version of that there too(I saw @jcsoo already has experiments with poll mechanism commented there)

@aep
Copy link

aep commented Aug 26, 2018

The current futures unfortunately lead to code size so big, it doesn't even work for some Linux based devices ( mips in my case with 4mb flash ).

I was thinking of going back to a less "modern" way and just using callbacks.

Will the upcoming async/await address the code bloat?

@japaric
Copy link
Member Author

japaric commented Nov 19, 2018

I meant to write a blog post on the topic of no_std cooperative multitasking earlier this year but I've been prioritizing other work. For now, I've cleaned up some code that was sitting on my disk: https://github.com/japaric/no-std-async-experiments .

The repo contains an executor of generators that supports concurrently running several infinite tasks; the executor puts the uC to sleep when no task can make progress; and interrupts can wake up tasks using signals.

The repo also contains an executor of futures based on core::{task,future} which is basically a port of the first executor; semantics are the same but this second version needs a bit more indirection to get the job done.

The repo contains an example, which you can run on your PC / laptop using QEMU, for each executor.

cc @Nemo157

@adamgreig
Copy link
Member

Closing this old discussion issue as part of today's triage of old issues. These days a number of multitasking models are in widespread use: RTOS bindings for classic threads (including popularly on ESP32, as well as native Rust RTOSs such as TockOS and Hubris), RTIC's interrupt-based multiple tasks, and async in both RTIC and Embassy. They're all widely used and seem to work out OK for people, so I don't think there's anything more for us to do today.

@adamgreig adamgreig closed this as not planned Won't fix, can't repro, duplicate, stale Jun 11, 2024
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

No branches or pull requests

7 participants