init project
This commit is contained in:
28
embassy-futures/Cargo.toml
Normal file
28
embassy-futures/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "embassy-futures"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
description = "no-std, no-alloc utilities for working with futures"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-futures"
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
categories = [
|
||||
"embedded",
|
||||
"no-std",
|
||||
"concurrency",
|
||||
"asynchronous",
|
||||
]
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-futures-v$VERSION/embassy-futures/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-futures/src/"
|
||||
features = ["defmt"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["defmt"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
13
embassy-futures/README.md
Normal file
13
embassy-futures/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# embassy-futures
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
Utilities for working with futures, compatible with `no_std` and not using `alloc`. Optimized for code size,
|
||||
ideal for embedded systems.
|
||||
|
||||
- Future combinators, like [`join`](join) and [`select`](select)
|
||||
- Utilities to use `async` without a fully fledged executor: [`block_on`](block_on::block_on) and [`yield_now`](yield_now::yield_now).
|
||||
|
||||
## Interoperability
|
||||
|
||||
Futures from this crate can run on any executor.
|
||||
45
embassy-futures/src/block_on.rs
Normal file
45
embassy-futures/src/block_on.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::ptr;
|
||||
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
static VTABLE: RawWakerVTable = RawWakerVTable::new(|_| RawWaker::new(ptr::null(), &VTABLE), |_| {}, |_| {}, |_| {});
|
||||
|
||||
/// Run a future to completion using a busy loop.
|
||||
///
|
||||
/// This calls `.poll()` on the future in a busy loop, which blocks
|
||||
/// the current thread at 100% cpu usage until the future is done. The
|
||||
/// future's `Waker` mechanism is not used.
|
||||
///
|
||||
/// You can use this to run multiple futures concurrently with [`join`][crate::join].
|
||||
///
|
||||
/// It's suitable for systems with no or limited concurrency and without
|
||||
/// strict requirements around power consumption. For more complex use
|
||||
/// cases, prefer using a "real" executor like `embassy-executor`, which
|
||||
/// supports multiple tasks, and putting the core to sleep when no task
|
||||
/// needs to do work.
|
||||
pub fn block_on<F: Future>(mut fut: F) -> F::Output {
|
||||
// safety: we don't move the future after this line.
|
||||
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
|
||||
|
||||
let raw_waker = RawWaker::new(ptr::null(), &VTABLE);
|
||||
let waker = unsafe { Waker::from_raw(raw_waker) };
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
loop {
|
||||
if let Poll::Ready(res) = fut.as_mut().poll(&mut cx) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll a future once.
|
||||
pub fn poll_once<F: Future>(mut fut: F) -> Poll<F::Output> {
|
||||
// safety: we don't move the future after this line.
|
||||
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
|
||||
|
||||
let raw_waker = RawWaker::new(ptr::null(), &VTABLE);
|
||||
let waker = unsafe { Waker::from_raw(raw_waker) };
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
fut.as_mut().poll(&mut cx)
|
||||
}
|
||||
270
embassy-futures/src/fmt.rs
Normal file
270
embassy-futures/src/fmt.rs
Normal file
@@ -0,0 +1,270 @@
|
||||
#![macro_use]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! debug_assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! debug_assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! debug_assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! todo {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::todo!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::todo!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! panic {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::panic!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::panic!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! trace {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::trace!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::trace!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! debug {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::debug!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! info {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::info!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::info!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! warn {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::warn!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::warn!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! error {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::error!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::error!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! unwrap {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unwrap!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
#[collapse_debuginfo(yes)]
|
||||
macro_rules! unwrap {
|
||||
($arg:expr) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct NoneError;
|
||||
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> Try for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:#02x?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Bytes<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:#02x?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LowerHex for Bytes<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:#02x?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl<'a> defmt::Format for Bytes<'a> {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(fmt, "{:02x}", self.0)
|
||||
}
|
||||
}
|
||||
322
embassy-futures/src/join.rs
Normal file
322
embassy-futures/src/join.rs
Normal file
@@ -0,0 +1,322 @@
|
||||
//! Wait for multiple futures to complete.
|
||||
|
||||
use core::future::Future;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::{fmt, mem};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MaybeDone<Fut: Future> {
|
||||
/// A not-yet-completed future
|
||||
Future(/* #[pin] */ Fut),
|
||||
/// The output of the completed future
|
||||
Done(Fut::Output),
|
||||
/// The empty variant after the result of a [`MaybeDone`] has been
|
||||
/// taken using the [`take_output`](MaybeDone::take_output) method.
|
||||
Gone,
|
||||
}
|
||||
|
||||
impl<Fut: Future> MaybeDone<Fut> {
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
match this {
|
||||
Self::Future(fut) => match unsafe { Pin::new_unchecked(fut) }.poll(cx) {
|
||||
Poll::Ready(res) => {
|
||||
*this = Self::Done(res);
|
||||
true
|
||||
}
|
||||
Poll::Pending => false,
|
||||
},
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn take_output(&mut self) -> Fut::Output {
|
||||
match &*self {
|
||||
Self::Done(_) => {}
|
||||
Self::Future(_) | Self::Gone => panic!("take_output when MaybeDone is not done."),
|
||||
}
|
||||
match mem::replace(self, Self::Gone) {
|
||||
MaybeDone::Done(output) => output,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Fut: Future + Unpin> Unpin for MaybeDone<Fut> {}
|
||||
|
||||
macro_rules! generate {
|
||||
($(
|
||||
$(#[$doc:meta])*
|
||||
($Join:ident, <$($Fut:ident),*>),
|
||||
)*) => ($(
|
||||
$(#[$doc])*
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
#[allow(non_snake_case)]
|
||||
pub struct $Join<$($Fut: Future),*> {
|
||||
$(
|
||||
$Fut: MaybeDone<$Fut>,
|
||||
)*
|
||||
}
|
||||
|
||||
impl<$($Fut),*> fmt::Debug for $Join<$($Fut),*>
|
||||
where
|
||||
$(
|
||||
$Fut: Future + fmt::Debug,
|
||||
$Fut::Output: fmt::Debug,
|
||||
)*
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct(stringify!($Join))
|
||||
$(.field(stringify!($Fut), &self.$Fut))*
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($Fut: Future),*> $Join<$($Fut),*> {
|
||||
#[allow(non_snake_case)]
|
||||
fn new($($Fut: $Fut),*) -> Self {
|
||||
Self {
|
||||
$($Fut: MaybeDone::Future($Fut)),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($Fut: Future),*> Future for $Join<$($Fut),*> {
|
||||
type Output = ($($Fut::Output),*);
|
||||
|
||||
fn poll(
|
||||
self: Pin<&mut Self>, cx: &mut Context<'_>
|
||||
) -> Poll<Self::Output> {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let mut all_done = true;
|
||||
$(
|
||||
all_done &= unsafe { Pin::new_unchecked(&mut this.$Fut) }.poll(cx);
|
||||
)*
|
||||
|
||||
if all_done {
|
||||
Poll::Ready(($(this.$Fut.take_output()), *))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
generate! {
|
||||
/// Future for the [`join`](join()) function.
|
||||
(Join, <Fut1, Fut2>),
|
||||
|
||||
/// Future for the [`join3`] function.
|
||||
(Join3, <Fut1, Fut2, Fut3>),
|
||||
|
||||
/// Future for the [`join4`] function.
|
||||
(Join4, <Fut1, Fut2, Fut3, Fut4>),
|
||||
|
||||
/// Future for the [`join5`] function.
|
||||
(Join5, <Fut1, Fut2, Fut3, Fut4, Fut5>),
|
||||
}
|
||||
|
||||
/// Joins the result of two futures, waiting for them both to complete.
|
||||
///
|
||||
/// This function will return a new future which awaits both futures to
|
||||
/// complete. The returned future will finish with a tuple of both results.
|
||||
///
|
||||
/// Note that this function consumes the passed futures and returns a
|
||||
/// wrapped version of it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # embassy_futures::block_on(async {
|
||||
///
|
||||
/// let a = async { 1 };
|
||||
/// let b = async { 2 };
|
||||
/// let pair = embassy_futures::join::join(a, b).await;
|
||||
///
|
||||
/// assert_eq!(pair, (1, 2));
|
||||
/// # });
|
||||
/// ```
|
||||
pub fn join<Fut1, Fut2>(future1: Fut1, future2: Fut2) -> Join<Fut1, Fut2>
|
||||
where
|
||||
Fut1: Future,
|
||||
Fut2: Future,
|
||||
{
|
||||
Join::new(future1, future2)
|
||||
}
|
||||
|
||||
/// Joins the result of three futures, waiting for them all to complete.
|
||||
///
|
||||
/// This function will return a new future which awaits all futures to
|
||||
/// complete. The returned future will finish with a tuple of all results.
|
||||
///
|
||||
/// Note that this function consumes the passed futures and returns a
|
||||
/// wrapped version of it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # embassy_futures::block_on(async {
|
||||
///
|
||||
/// let a = async { 1 };
|
||||
/// let b = async { 2 };
|
||||
/// let c = async { 3 };
|
||||
/// let res = embassy_futures::join::join3(a, b, c).await;
|
||||
///
|
||||
/// assert_eq!(res, (1, 2, 3));
|
||||
/// # });
|
||||
/// ```
|
||||
pub fn join3<Fut1, Fut2, Fut3>(future1: Fut1, future2: Fut2, future3: Fut3) -> Join3<Fut1, Fut2, Fut3>
|
||||
where
|
||||
Fut1: Future,
|
||||
Fut2: Future,
|
||||
Fut3: Future,
|
||||
{
|
||||
Join3::new(future1, future2, future3)
|
||||
}
|
||||
|
||||
/// Joins the result of four futures, waiting for them all to complete.
|
||||
///
|
||||
/// This function will return a new future which awaits all futures to
|
||||
/// complete. The returned future will finish with a tuple of all results.
|
||||
///
|
||||
/// Note that this function consumes the passed futures and returns a
|
||||
/// wrapped version of it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # embassy_futures::block_on(async {
|
||||
///
|
||||
/// let a = async { 1 };
|
||||
/// let b = async { 2 };
|
||||
/// let c = async { 3 };
|
||||
/// let d = async { 4 };
|
||||
/// let res = embassy_futures::join::join4(a, b, c, d).await;
|
||||
///
|
||||
/// assert_eq!(res, (1, 2, 3, 4));
|
||||
/// # });
|
||||
/// ```
|
||||
pub fn join4<Fut1, Fut2, Fut3, Fut4>(
|
||||
future1: Fut1,
|
||||
future2: Fut2,
|
||||
future3: Fut3,
|
||||
future4: Fut4,
|
||||
) -> Join4<Fut1, Fut2, Fut3, Fut4>
|
||||
where
|
||||
Fut1: Future,
|
||||
Fut2: Future,
|
||||
Fut3: Future,
|
||||
Fut4: Future,
|
||||
{
|
||||
Join4::new(future1, future2, future3, future4)
|
||||
}
|
||||
|
||||
/// Joins the result of five futures, waiting for them all to complete.
|
||||
///
|
||||
/// This function will return a new future which awaits all futures to
|
||||
/// complete. The returned future will finish with a tuple of all results.
|
||||
///
|
||||
/// Note that this function consumes the passed futures and returns a
|
||||
/// wrapped version of it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # embassy_futures::block_on(async {
|
||||
///
|
||||
/// let a = async { 1 };
|
||||
/// let b = async { 2 };
|
||||
/// let c = async { 3 };
|
||||
/// let d = async { 4 };
|
||||
/// let e = async { 5 };
|
||||
/// let res = embassy_futures::join::join5(a, b, c, d, e).await;
|
||||
///
|
||||
/// assert_eq!(res, (1, 2, 3, 4, 5));
|
||||
/// # });
|
||||
/// ```
|
||||
pub fn join5<Fut1, Fut2, Fut3, Fut4, Fut5>(
|
||||
future1: Fut1,
|
||||
future2: Fut2,
|
||||
future3: Fut3,
|
||||
future4: Fut4,
|
||||
future5: Fut5,
|
||||
) -> Join5<Fut1, Fut2, Fut3, Fut4, Fut5>
|
||||
where
|
||||
Fut1: Future,
|
||||
Fut2: Future,
|
||||
Fut3: Future,
|
||||
Fut4: Future,
|
||||
Fut5: Future,
|
||||
{
|
||||
Join5::new(future1, future2, future3, future4, future5)
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
|
||||
/// Future for the [`join_array`] function.
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct JoinArray<Fut: Future, const N: usize> {
|
||||
futures: [MaybeDone<Fut>; N],
|
||||
}
|
||||
|
||||
impl<Fut: Future, const N: usize> fmt::Debug for JoinArray<Fut, N>
|
||||
where
|
||||
Fut: Future + fmt::Debug,
|
||||
Fut::Output: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("JoinArray").field("futures", &self.futures).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Fut: Future, const N: usize> Future for JoinArray<Fut, N> {
|
||||
type Output = [Fut::Output; N];
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let mut all_done = true;
|
||||
for f in this.futures.iter_mut() {
|
||||
all_done &= unsafe { Pin::new_unchecked(f) }.poll(cx);
|
||||
}
|
||||
|
||||
if all_done {
|
||||
let mut array: [MaybeUninit<Fut::Output>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
for i in 0..N {
|
||||
array[i].write(this.futures[i].take_output());
|
||||
}
|
||||
Poll::Ready(unsafe { (&array as *const _ as *const [Fut::Output; N]).read() })
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Joins the result of an array of futures, waiting for them all to complete.
|
||||
///
|
||||
/// This function will return a new future which awaits all futures to
|
||||
/// complete. The returned future will finish with a tuple of all results.
|
||||
///
|
||||
/// Note that this function consumes the passed futures and returns a
|
||||
/// wrapped version of it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # embassy_futures::block_on(async {
|
||||
///
|
||||
/// async fn foo(n: u32) -> u32 { n }
|
||||
/// let a = foo(1);
|
||||
/// let b = foo(2);
|
||||
/// let c = foo(3);
|
||||
/// let res = embassy_futures::join::join_array([a, b, c]).await;
|
||||
///
|
||||
/// assert_eq!(res, [1, 2, 3]);
|
||||
/// # });
|
||||
/// ```
|
||||
pub fn join_array<Fut: Future, const N: usize>(futures: [Fut; N]) -> JoinArray<Fut, N> {
|
||||
JoinArray {
|
||||
futures: futures.map(MaybeDone::Future),
|
||||
}
|
||||
}
|
||||
15
embassy-futures/src/lib.rs
Normal file
15
embassy-futures/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
#![no_std]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
mod block_on;
|
||||
mod yield_now;
|
||||
|
||||
pub mod join;
|
||||
pub mod select;
|
||||
|
||||
pub use block_on::*;
|
||||
pub use yield_now::*;
|
||||
545
embassy-futures/src/select.rs
Normal file
545
embassy-futures/src/select.rs
Normal file
@@ -0,0 +1,545 @@
|
||||
//! Wait for the first of several futures to complete.
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
/// Result for [`select`].
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Either<A, B> {
|
||||
/// First future finished first.
|
||||
First(A),
|
||||
/// Second future finished first.
|
||||
Second(B),
|
||||
}
|
||||
|
||||
impl<A, B> Either<A, B> {
|
||||
/// Did the first future complete first?
|
||||
pub fn is_first(&self) -> bool {
|
||||
matches!(self, Either::First(_))
|
||||
}
|
||||
|
||||
/// Did the second future complete first?
|
||||
pub fn is_second(&self) -> bool {
|
||||
matches!(self, Either::Second(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for one of two futures to complete.
|
||||
///
|
||||
/// This function returns a new future which polls all the futures.
|
||||
/// When one of them completes, it will complete with its result value.
|
||||
///
|
||||
/// The other future is dropped.
|
||||
pub fn select<A, B>(a: A, b: B) -> Select<A, B>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
{
|
||||
Select { a, b }
|
||||
}
|
||||
|
||||
/// Future for the [`select`] function.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Select<A, B> {
|
||||
a: A,
|
||||
b: B,
|
||||
}
|
||||
|
||||
impl<A: Unpin, B: Unpin> Unpin for Select<A, B> {}
|
||||
|
||||
impl<A, B> Future for Select<A, B>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
{
|
||||
type Output = Either<A::Output, B::Output>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let a = unsafe { Pin::new_unchecked(&mut this.a) };
|
||||
let b = unsafe { Pin::new_unchecked(&mut this.b) };
|
||||
if let Poll::Ready(x) = a.poll(cx) {
|
||||
return Poll::Ready(Either::First(x));
|
||||
}
|
||||
if let Poll::Ready(x) = b.poll(cx) {
|
||||
return Poll::Ready(Either::Second(x));
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
|
||||
/// Result for [`select3`].
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Either3<A, B, C> {
|
||||
/// First future finished first.
|
||||
First(A),
|
||||
/// Second future finished first.
|
||||
Second(B),
|
||||
/// Third future finished first.
|
||||
Third(C),
|
||||
}
|
||||
|
||||
impl<A, B, C> Either3<A, B, C> {
|
||||
/// Did the first future complete first?
|
||||
pub fn is_first(&self) -> bool {
|
||||
matches!(self, Either3::First(_))
|
||||
}
|
||||
|
||||
/// Did the second future complete first?
|
||||
pub fn is_second(&self) -> bool {
|
||||
matches!(self, Either3::Second(_))
|
||||
}
|
||||
|
||||
/// Did the third future complete first?
|
||||
pub fn is_third(&self) -> bool {
|
||||
matches!(self, Either3::Third(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as [`select`], but with more futures.
|
||||
pub fn select3<A, B, C>(a: A, b: B, c: C) -> Select3<A, B, C>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
C: Future,
|
||||
{
|
||||
Select3 { a, b, c }
|
||||
}
|
||||
|
||||
/// Future for the [`select3`] function.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Select3<A, B, C> {
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
}
|
||||
|
||||
impl<A, B, C> Future for Select3<A, B, C>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
C: Future,
|
||||
{
|
||||
type Output = Either3<A::Output, B::Output, C::Output>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let a = unsafe { Pin::new_unchecked(&mut this.a) };
|
||||
let b = unsafe { Pin::new_unchecked(&mut this.b) };
|
||||
let c = unsafe { Pin::new_unchecked(&mut this.c) };
|
||||
if let Poll::Ready(x) = a.poll(cx) {
|
||||
return Poll::Ready(Either3::First(x));
|
||||
}
|
||||
if let Poll::Ready(x) = b.poll(cx) {
|
||||
return Poll::Ready(Either3::Second(x));
|
||||
}
|
||||
if let Poll::Ready(x) = c.poll(cx) {
|
||||
return Poll::Ready(Either3::Third(x));
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
|
||||
/// Result for [`select4`].
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Either4<A, B, C, D> {
|
||||
/// First future finished first.
|
||||
First(A),
|
||||
/// Second future finished first.
|
||||
Second(B),
|
||||
/// Third future finished first.
|
||||
Third(C),
|
||||
/// Fourth future finished first.
|
||||
Fourth(D),
|
||||
}
|
||||
|
||||
impl<A, B, C, D> Either4<A, B, C, D> {
|
||||
/// Did the first future complete first?
|
||||
pub fn is_first(&self) -> bool {
|
||||
matches!(self, Either4::First(_))
|
||||
}
|
||||
|
||||
/// Did the second future complete first?
|
||||
pub fn is_second(&self) -> bool {
|
||||
matches!(self, Either4::Second(_))
|
||||
}
|
||||
|
||||
/// Did the third future complete first?
|
||||
pub fn is_third(&self) -> bool {
|
||||
matches!(self, Either4::Third(_))
|
||||
}
|
||||
|
||||
/// Did the fourth future complete first?
|
||||
pub fn is_fourth(&self) -> bool {
|
||||
matches!(self, Either4::Fourth(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as [`select`], but with more futures.
|
||||
pub fn select4<A, B, C, D>(a: A, b: B, c: C, d: D) -> Select4<A, B, C, D>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
C: Future,
|
||||
D: Future,
|
||||
{
|
||||
Select4 { a, b, c, d }
|
||||
}
|
||||
|
||||
/// Future for the [`select4`] function.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Select4<A, B, C, D> {
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
}
|
||||
|
||||
impl<A, B, C, D> Future for Select4<A, B, C, D>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
C: Future,
|
||||
D: Future,
|
||||
{
|
||||
type Output = Either4<A::Output, B::Output, C::Output, D::Output>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let a = unsafe { Pin::new_unchecked(&mut this.a) };
|
||||
let b = unsafe { Pin::new_unchecked(&mut this.b) };
|
||||
let c = unsafe { Pin::new_unchecked(&mut this.c) };
|
||||
let d = unsafe { Pin::new_unchecked(&mut this.d) };
|
||||
if let Poll::Ready(x) = a.poll(cx) {
|
||||
return Poll::Ready(Either4::First(x));
|
||||
}
|
||||
if let Poll::Ready(x) = b.poll(cx) {
|
||||
return Poll::Ready(Either4::Second(x));
|
||||
}
|
||||
if let Poll::Ready(x) = c.poll(cx) {
|
||||
return Poll::Ready(Either4::Third(x));
|
||||
}
|
||||
if let Poll::Ready(x) = d.poll(cx) {
|
||||
return Poll::Ready(Either4::Fourth(x));
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
|
||||
/// Result for [`select5`].
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Either5<A, B, C, D, E> {
|
||||
/// First future finished first.
|
||||
First(A),
|
||||
/// Second future finished first.
|
||||
Second(B),
|
||||
/// Third future finished first.
|
||||
Third(C),
|
||||
/// Fourth future finished first.
|
||||
Fourth(D),
|
||||
/// Fifth future finished first.
|
||||
Fifth(E),
|
||||
}
|
||||
|
||||
impl<A, B, C, D, E> Either5<A, B, C, D, E> {
|
||||
/// Did the first future complete first?
|
||||
pub fn is_first(&self) -> bool {
|
||||
matches!(self, Either5::First(_))
|
||||
}
|
||||
|
||||
/// Did the second future complete first?
|
||||
pub fn is_second(&self) -> bool {
|
||||
matches!(self, Either5::Second(_))
|
||||
}
|
||||
|
||||
/// Did the third future complete first?
|
||||
pub fn is_third(&self) -> bool {
|
||||
matches!(self, Either5::Third(_))
|
||||
}
|
||||
|
||||
/// Did the fourth future complete first?
|
||||
pub fn is_fourth(&self) -> bool {
|
||||
matches!(self, Either5::Fourth(_))
|
||||
}
|
||||
|
||||
/// Did the fifth future complete first?
|
||||
pub fn is_fifth(&self) -> bool {
|
||||
matches!(self, Either5::Fifth(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as [`select`], but with more futures.
|
||||
pub fn select5<A, B, C, D, E>(a: A, b: B, c: C, d: D, e: E) -> Select5<A, B, C, D, E>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
C: Future,
|
||||
D: Future,
|
||||
E: Future,
|
||||
{
|
||||
Select5 { a, b, c, d, e }
|
||||
}
|
||||
|
||||
/// Future for the [`select5`] function.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Select5<A, B, C, D, E> {
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
}
|
||||
|
||||
impl<A, B, C, D, E> Future for Select5<A, B, C, D, E>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
C: Future,
|
||||
D: Future,
|
||||
E: Future,
|
||||
{
|
||||
type Output = Either5<A::Output, B::Output, C::Output, D::Output, E::Output>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let a = unsafe { Pin::new_unchecked(&mut this.a) };
|
||||
let b = unsafe { Pin::new_unchecked(&mut this.b) };
|
||||
let c = unsafe { Pin::new_unchecked(&mut this.c) };
|
||||
let d = unsafe { Pin::new_unchecked(&mut this.d) };
|
||||
let e = unsafe { Pin::new_unchecked(&mut this.e) };
|
||||
if let Poll::Ready(x) = a.poll(cx) {
|
||||
return Poll::Ready(Either5::First(x));
|
||||
}
|
||||
if let Poll::Ready(x) = b.poll(cx) {
|
||||
return Poll::Ready(Either5::Second(x));
|
||||
}
|
||||
if let Poll::Ready(x) = c.poll(cx) {
|
||||
return Poll::Ready(Either5::Third(x));
|
||||
}
|
||||
if let Poll::Ready(x) = d.poll(cx) {
|
||||
return Poll::Ready(Either5::Fourth(x));
|
||||
}
|
||||
if let Poll::Ready(x) = e.poll(cx) {
|
||||
return Poll::Ready(Either5::Fifth(x));
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
|
||||
/// Result for [`select6`].
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Either6<A, B, C, D, E, F> {
|
||||
/// First future finished first.
|
||||
First(A),
|
||||
/// Second future finished first.
|
||||
Second(B),
|
||||
/// Third future finished first.
|
||||
Third(C),
|
||||
/// Fourth future finished first.
|
||||
Fourth(D),
|
||||
/// Fifth future finished first.
|
||||
Fifth(E),
|
||||
/// Sixth future finished first.
|
||||
Sixth(F),
|
||||
}
|
||||
|
||||
impl<A, B, C, D, E, F> Either6<A, B, C, D, E, F> {
|
||||
/// Did the first future complete first?
|
||||
pub fn is_first(&self) -> bool {
|
||||
matches!(self, Either6::First(_))
|
||||
}
|
||||
|
||||
/// Did the second future complete first?
|
||||
pub fn is_second(&self) -> bool {
|
||||
matches!(self, Either6::Second(_))
|
||||
}
|
||||
|
||||
/// Did the third future complete first?
|
||||
pub fn is_third(&self) -> bool {
|
||||
matches!(self, Either6::Third(_))
|
||||
}
|
||||
|
||||
/// Did the fourth future complete first?
|
||||
pub fn is_fourth(&self) -> bool {
|
||||
matches!(self, Either6::Fourth(_))
|
||||
}
|
||||
|
||||
/// Did the fifth future complete first?
|
||||
pub fn is_fifth(&self) -> bool {
|
||||
matches!(self, Either6::Fifth(_))
|
||||
}
|
||||
|
||||
/// Did the sixth future complete first?
|
||||
pub fn is_sixth(&self) -> bool {
|
||||
matches!(self, Either6::Sixth(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as [`select`], but with more futures.
|
||||
pub fn select6<A, B, C, D, E, F>(a: A, b: B, c: C, d: D, e: E, f: F) -> Select6<A, B, C, D, E, F>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
C: Future,
|
||||
D: Future,
|
||||
E: Future,
|
||||
F: Future,
|
||||
{
|
||||
Select6 { a, b, c, d, e, f }
|
||||
}
|
||||
|
||||
/// Future for the [`select6`] function.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Select6<A, B, C, D, E, F> {
|
||||
a: A,
|
||||
b: B,
|
||||
c: C,
|
||||
d: D,
|
||||
e: E,
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<A, B, C, D, E, F> Future for Select6<A, B, C, D, E, F>
|
||||
where
|
||||
A: Future,
|
||||
B: Future,
|
||||
C: Future,
|
||||
D: Future,
|
||||
E: Future,
|
||||
F: Future,
|
||||
{
|
||||
type Output = Either6<A::Output, B::Output, C::Output, D::Output, E::Output, F::Output>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let a = unsafe { Pin::new_unchecked(&mut this.a) };
|
||||
let b = unsafe { Pin::new_unchecked(&mut this.b) };
|
||||
let c = unsafe { Pin::new_unchecked(&mut this.c) };
|
||||
let d = unsafe { Pin::new_unchecked(&mut this.d) };
|
||||
let e = unsafe { Pin::new_unchecked(&mut this.e) };
|
||||
let f = unsafe { Pin::new_unchecked(&mut this.f) };
|
||||
if let Poll::Ready(x) = a.poll(cx) {
|
||||
return Poll::Ready(Either6::First(x));
|
||||
}
|
||||
if let Poll::Ready(x) = b.poll(cx) {
|
||||
return Poll::Ready(Either6::Second(x));
|
||||
}
|
||||
if let Poll::Ready(x) = c.poll(cx) {
|
||||
return Poll::Ready(Either6::Third(x));
|
||||
}
|
||||
if let Poll::Ready(x) = d.poll(cx) {
|
||||
return Poll::Ready(Either6::Fourth(x));
|
||||
}
|
||||
if let Poll::Ready(x) = e.poll(cx) {
|
||||
return Poll::Ready(Either6::Fifth(x));
|
||||
}
|
||||
if let Poll::Ready(x) = f.poll(cx) {
|
||||
return Poll::Ready(Either6::Sixth(x));
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
|
||||
/// Future for the [`select_array`] function.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct SelectArray<Fut, const N: usize> {
|
||||
inner: [Fut; N],
|
||||
}
|
||||
|
||||
/// Creates a new future which will select over an array of futures.
|
||||
///
|
||||
/// The returned future will wait for any future to be ready. Upon
|
||||
/// completion the item resolved will be returned, along with the index of the
|
||||
/// future that was ready.
|
||||
///
|
||||
/// If the array is empty, the resulting future will be Pending forever.
|
||||
pub fn select_array<Fut: Future, const N: usize>(arr: [Fut; N]) -> SelectArray<Fut, N> {
|
||||
SelectArray { inner: arr }
|
||||
}
|
||||
|
||||
impl<Fut: Future, const N: usize> Future for SelectArray<Fut, N> {
|
||||
type Output = (Fut::Output, usize);
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// Safety: Since `self` is pinned, `inner` cannot move. Since `inner` cannot move,
|
||||
// its elements also cannot move. Therefore it is safe to access `inner` and pin
|
||||
// references to the contained futures.
|
||||
let item = unsafe {
|
||||
self.get_unchecked_mut()
|
||||
.inner
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.find_map(|(i, f)| match Pin::new_unchecked(f).poll(cx) {
|
||||
Poll::Pending => None,
|
||||
Poll::Ready(e) => Some((i, e)),
|
||||
})
|
||||
};
|
||||
|
||||
match item {
|
||||
Some((idx, res)) => Poll::Ready((res, idx)),
|
||||
None => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
|
||||
/// Future for the [`select_slice`] function.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct SelectSlice<'a, Fut> {
|
||||
inner: Pin<&'a mut [Fut]>,
|
||||
}
|
||||
|
||||
/// Creates a new future which will select over a slice of futures.
|
||||
///
|
||||
/// The returned future will wait for any future to be ready. Upon
|
||||
/// completion the item resolved will be returned, along with the index of the
|
||||
/// future that was ready.
|
||||
///
|
||||
/// If the slice is empty, the resulting future will be Pending forever.
|
||||
pub fn select_slice<'a, Fut: Future>(slice: Pin<&'a mut [Fut]>) -> SelectSlice<'a, Fut> {
|
||||
SelectSlice { inner: slice }
|
||||
}
|
||||
|
||||
impl<'a, Fut: Future> Future for SelectSlice<'a, Fut> {
|
||||
type Output = (Fut::Output, usize);
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// Safety: refer to
|
||||
// https://users.rust-lang.org/t/working-with-pinned-slices-are-there-any-structurally-pinning-vec-like-collection-types/50634/2
|
||||
#[inline(always)]
|
||||
fn pin_iter<T>(slice: Pin<&mut [T]>) -> impl Iterator<Item = Pin<&mut T>> {
|
||||
unsafe { slice.get_unchecked_mut().iter_mut().map(|v| Pin::new_unchecked(v)) }
|
||||
}
|
||||
for (i, fut) in pin_iter(self.inner.as_mut()).enumerate() {
|
||||
if let Poll::Ready(res) = fut.poll(cx) {
|
||||
return Poll::Ready((res, i));
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
49
embassy-futures/src/yield_now.rs
Normal file
49
embassy-futures/src/yield_now.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
/// Yield from the current task once, allowing other tasks to run.
|
||||
///
|
||||
/// This can be used to easily and quickly implement simple async primitives
|
||||
/// without using wakers. The following snippet will wait for a condition to
|
||||
/// hold, while still allowing other tasks to run concurrently (not monopolizing
|
||||
/// the executor thread).
|
||||
///
|
||||
/// ```rust
|
||||
/// # use embassy_futures::{block_on, yield_now};
|
||||
/// # async fn test_fn() {
|
||||
/// # let mut iter_count: u32 = 0;
|
||||
/// # let mut some_condition = || { iter_count += 1; iter_count > 10 };
|
||||
/// while !some_condition() {
|
||||
/// yield_now().await;
|
||||
/// }
|
||||
/// # }
|
||||
/// # block_on(test_fn());
|
||||
/// ```
|
||||
///
|
||||
/// The downside is this will spin in a busy loop, using 100% of the CPU, while
|
||||
/// using wakers correctly would allow the CPU to sleep while waiting.
|
||||
///
|
||||
/// The internal implementation is: on first poll the future wakes itself and
|
||||
/// returns `Poll::Pending`. On second poll, it returns `Poll::Ready`.
|
||||
pub fn yield_now() -> impl Future<Output = ()> {
|
||||
YieldNowFuture { yielded: false }
|
||||
}
|
||||
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
struct YieldNowFuture {
|
||||
yielded: bool,
|
||||
}
|
||||
|
||||
impl Future for YieldNowFuture {
|
||||
type Output = ();
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if self.yielded {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
self.yielded = true;
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user