Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use Tokio Reactor in a #[no_std] environment?

I am trying to implement futures on the Tock OS embedded operating system. I'm trying to using Tokio in a #[no_std] environment.

My Cargo.toml file looks like this:

[package]
name = "nrf52dk"
version = "0.1.0"
authors = ["Tock Project Developers <[email protected]>"]
build = "build.rs"

[profile.dev]
panic = "abort"
lto = true
opt-level = "z"
debug = true

[profile.release]
panic = "abort"
lto = true
opt-level = "z"
debug = true

[dependencies]
cortexm4 = { path = "../../arch/cortex-m4" }
capsules = { path = "../../capsules" }
kernel = { path = "../../kernel" }
nrf52 = { path = "../../chips/nrf52" }
nrf5x = { path = "../../chips/nrf5x" }
futures = {version = "0.2.0", default-features = false }

This compiles with no errors but when I add tokio-reactor = "0.1.1", I get the error: error[E0463]: can't find crate for std. I understand this is because Tokio imports some stuff from the std library.

Is it possible to get around this problem?

like image 879
EmbeddedOS Avatar asked Jan 03 '23 12:01

EmbeddedOS


2 Answers

As far as I can tell, you don't. Tokio Reactor 0.1.1 imports many things from the standard library, none of them conditionally.

Most of the imports could probably be switched to libcore alternatives, but Arc requires memory allocation, which lives in the alloc crate.

As an example of a crate that supports no_std, check out Futures 0.1.20. This has a feature flag to opt-in to functionality that requires the standard library.

If you wish to do this, you'll need to contribute substantial effort to Tokio and all of its dependencies to add feature flags to opt-in to all the functionality that requires the standard library. It would be worth opening an issue with the maintainers to coordinate such an effort.

like image 101
Shepmaster Avatar answered Jan 05 '23 00:01

Shepmaster


Expanding on what Shepmaster already said: you don't want tokio; it's based on mio, which is unlikely to ever work in a kernel, especially without heap allocation / std.

So how to drive tasks (spawned Futures) in such environment (this is written for the futures 0.1.x series):

  • your "Executor" ("main loop") will want to track some state per task, e.g. whether you need to poll it, perhaps some linked list to find those that need to be polled.
  • you need a place for that state; you also need to store the Futures wrapped in Spawn<...>. It should be possible to use "static" allocated storage for that.
  • you'll need to implement UnsafeNotify (and the base trait Notify), probably for some raw pointer/&'static reference to the task (including the state); notify needs to be able to queue tasks to get polled in a thread safe way. The {clone,drop}_{raw,id} functions can be empty as you'll be using static allocations anyway. notify also needs to schedule the main loop if it is sleeping. The queue itself will need some global state too ("list head+tail"); if you need different queues you can store a reference to it too in a NotifyHandle (e.g. in the id: usize parameter).
  • you could even try running multiple loops on the same "poll queue", good luck getting it thread-safe :) The future-0.2 ThreadPool might give some ideas how to do that (or the tokio-threadpool crate).
  • you'll probably need to add some "timer" handling to the event loop; a timer should store a NotifyHandle to the task it is supposed to wake on a timeout, some state to track whether the timeout was hit, and the event loop needs a list of active (pointers to) timers to determine how long to wait. (the tokio-timer crate might give you some ideas how to implement this)
  • some similar handling for async IO; in userspace you'd use select with a timeout (or platform specific optimized version of it), in a kernel you'll probably have to find other ways :) (In the tokio world this is provided by the Reactor, which is based on mio)
  • to drive a task you'll want to use poll_future_notify

In futures-0.2 NotifyHandle became Waker, and UnsafeNotify became UnsafeWake; the id: usize context is gone (just use a struct with all the data you need to implement UnsafeWake for). Instead of storing Spawn<...> for a future you need to manually store a LocalMap for each task, which is then be used to create a Context with Context::without_spawn, which is then passed to Future::poll.

like image 37
Stefan Avatar answered Jan 05 '23 00:01

Stefan