Skip to content

Commit dd6e3a7

Browse files
authored
Timer type (#4)
* Timer type * Minor cleanup; clarify Timer type use cases * Clarify Timer precision * More small updates to the README * More small updates to the README * More small updates to the README
1 parent 6378246 commit dd6e3a7

File tree

6 files changed

+1817
-1232
lines changed

6 files changed

+1817
-1232
lines changed

Cargo.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ categories = ["asynchronous", "network-programming", "MCU", "embedded"]
1212
exclude = ["/.*"]
1313

1414
[features]
15-
default = ["futures-io", "futures-lite"]
15+
default = ["futures-io", "futures-lite", "embassy-time"]
16+
embassy-time = ["embassy-time-driver", "embassy-time-queue-driver", "dep:embassy-time"]
1617

1718
[dependencies]
1819
libc = "0.2"
@@ -21,6 +22,8 @@ heapless = "0.8"
2122
log = { version = "0.4", default-features = false }
2223
futures-io = { version = "0.3", default-features = false, optional = true, features = ["std"] }
2324
futures-lite = { version = "1", default-features = false, optional = true }
25+
embassy-time-driver = { version = "0.1", optional = true }
26+
embassy-time-queue-driver = { version = "0.1", optional = true }
2427
embassy-time = { version = "0.3", optional = true }
2528

2629
[dev-dependencies]
@@ -32,4 +35,8 @@ env_logger = "0.10"
3235

3336
[[test]]
3437
name = "async"
35-
required-features = ["futures-io", "futures-lite"]
38+
required-features = ["futures-io", "futures-lite", "embassy-time"]
39+
40+
[[test]]
41+
name = "timer"
42+
required-features = ["futures-lite", "embassy-time"]

README.md

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
[![Cargo](https://img.shields.io/crates/v/async-io-mini.svg)](https://crates.io/crates/async-io-mini)
66
[![Documentation](https://docs.rs/async-io/badge.svg)](https://docs.rs/async-io-mini)
77

8-
Async I/O. **EXPERIMENTAL!!**
8+
Async I/O and timers. **Experimental**
99

10-
This crate is an **experimental** fork of the splendid [`async-io`](https://github.com/smol-rs/async-io) crate targetting MCUs and ESP-IDF in particular.
10+
This crate is an experimental fork of the splendid [`async-io`](https://github.com/smol-rs/async-io) crate targetting MCUs and ESP-IDF in particular.
1111

1212
## How to use?
1313

14-
`async-io-mini` is a drop-in, API-compatible replacement for the `Async` type from `async-io` (but does NOT have an equivalent of `Timer` - see why [below](#limitations)).
14+
`async-io-mini` is a drop-in, API-compatible replacement for the `Async` and `Timer` types from `async-io`.
1515

1616
So either:
1717
* Just replace all `use async_io` occurances in your crate with `use async_io_mini`
@@ -37,40 +37,53 @@ Further, `async-io` has a non-trivial set of dependencies (again - for MCUs; for
3737
- `log` (might become optional);
3838
- `enumset` (not crucial, might remove).
3939

40-
## Limitations
40+
## Enhancements
41+
42+
The `Timer` type of `async_io_mini` is based on the `embassy-time` crate, and as such should offer a higher resolution on embedded operating systems like the ESP-IDF than what can be normally achieved by implementing timers using the `timeout` parameter of the `select` syscall (as `async-io` does).
4143

42-
### No timers
44+
The reason for this is that on the ESP-IDF, the `timeout` parameter of `select` provides a resolution of 10ms (one FreeRTOS sys-tick), while
45+
`embassy-time` is implemented using the [ESP-IDF Timer service](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/esp_timer.html), which provides resolutions up to 1 microsecond.
4346

44-
`async-io-mini` does NOT have an equivalent of `async_io::Timer`. On ESP-IDF at least, timers based on OS systicks are often not very useful, as the OS systick is low-res (10ms).
47+
With that said, for greenfield code that does not need to be compatible with `async-io`, use the native `embassy_time::Timer` and `embassy_time::Ticker` rather than `async_io_mini::Timer`, because the latter has a larger memory footprint (40 bytes on 32bit archs) compared to the `embassy-time` types (8 and 16 bytes each).
4548

46-
Workaround: use the `Timer` struct from the [`embassy-time`](https://crates.io/crates/embassy-time) crate, which provides a very similar API and is highly optimized for embedded environments. On the ESP-IDF, the `embassy-time-driver` implementation is backed by the ESP-IDF Timer service, which runs off from a high priority thread by default and thus has good res.
49+
## Limitations
4750

4851
### No equivalent of `async_io::block_on`
4952

5053
Implementing socket polling as a shared task between the hidden `async-io-mini` thread and the thread calling `async_io_mini::block_on` is not trivial and probably not worth it on MCUs. Just use `futures_lite::block_on` or the `block_on` equivalent for your OS (i.e. `esp_idf_svc::hal::task::block_on` for the ESP-IDF).
5154

5255
## Implementation
5356

57+
### Async
58+
5459
The first time `Async` is used, a thread named `async-io-mini` will be spawned.
5560
The purpose of this thread is to wait for I/O events reported by the operating system, and then
5661
wake appropriate futures blocked on I/O when they can be resumed.
5762

5863
To wait for the next I/O event, the "async-io-mini" thread uses the [select](https://en.wikipedia.org/wiki/Select_(Unix)) syscall, and **is thus only useful for MCUs (might just be the ESP-IDF) where the number of file or socket handles is very small anyway**.
5964

65+
### Timer
66+
67+
As per above, the `Timer` type is a wrapper around the functionality provided by the `embassy-time` crate.
68+
6069
## Examples
6170

62-
Connect to `example.com:80`.
71+
Connect to `example.com:80`, or time out after 10 seconds.
6372

6473
```rust
65-
use async_io_mini::Async;
74+
use async_io_mini::{Async, Timer};
6675
use futures_lite::{future::FutureExt, io};
6776

6877
use std::net::{TcpStream, ToSocketAddrs};
6978
use std::time::Duration;
7079

7180
let addr = "example.com:80".to_socket_addrs()?.next().unwrap();
7281

73-
let stream = Async::<TcpStream>::connect(addr).await?;
82+
let stream = Async::<TcpStream>::connect(addr).or(async {
83+
Timer::after(Duration::from_secs(10)).await;
84+
Err(io::ErrorKind::TimedOut.into())
85+
})
86+
.await?;
7487
```
7588

7689
## License

0 commit comments

Comments
 (0)