summaryrefslogtreecommitdiff
path: root/embassy-futures
diff options
context:
space:
mode:
authorDario Nieuwenhuis <dirbaio@dirbaio.net>2022-08-22 21:46:09 +0200
committerDario Nieuwenhuis <dirbaio@dirbaio.net>2022-08-22 22:18:13 +0200
commit21072bee48ff6ec19b79e0d9527ad8cc34a4e9e0 (patch)
treeb5b8c0f4b3571989b5fd15152be5639f4334c282 /embassy-futures
parent61356181b223e95f289ca3af3a038a699cde2112 (diff)
downloadembassy-21072bee48ff6ec19b79e0d9527ad8cc34a4e9e0.zip
split `embassy-util` into `embassy-futures`, `embassy-sync`.
Diffstat (limited to 'embassy-futures')
-rw-r--r--embassy-futures/Cargo.toml14
-rw-r--r--embassy-futures/src/fmt.rs228
-rw-r--r--embassy-futures/src/lib.rs12
-rw-r--r--embassy-futures/src/select.rs230
-rw-r--r--embassy-futures/src/yield_now.rs25
5 files changed, 509 insertions, 0 deletions
diff --git a/embassy-futures/Cargo.toml b/embassy-futures/Cargo.toml
new file mode 100644
index 00000000..e564f5a9
--- /dev/null
+++ b/embassy-futures/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "embassy-futures"
+version = "0.1.0"
+edition = "2021"
+
+[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 = ["nightly"]
+target = "thumbv7em-none-eabi"
+
+[dependencies]
+defmt = { version = "0.3", optional = true }
+log = { version = "0.4.14", optional = true }
diff --git a/embassy-futures/src/fmt.rs b/embassy-futures/src/fmt.rs
new file mode 100644
index 00000000..f8bb0a03
--- /dev/null
+++ b/embassy-futures/src/fmt.rs
@@ -0,0 +1,228 @@
+#![macro_use]
+#![allow(unused_macros)]
+
+#[cfg(all(feature = "defmt", feature = "log"))]
+compile_error!("You may not enable both `defmt` and `log` features.");
+
+macro_rules! assert {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::assert!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::assert!($($x)*);
+ }
+ };
+}
+
+macro_rules! assert_eq {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::assert_eq!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::assert_eq!($($x)*);
+ }
+ };
+}
+
+macro_rules! assert_ne {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::assert_ne!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::assert_ne!($($x)*);
+ }
+ };
+}
+
+macro_rules! debug_assert {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::debug_assert!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug_assert!($($x)*);
+ }
+ };
+}
+
+macro_rules! debug_assert_eq {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::debug_assert_eq!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug_assert_eq!($($x)*);
+ }
+ };
+}
+
+macro_rules! debug_assert_ne {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::debug_assert_ne!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug_assert_ne!($($x)*);
+ }
+ };
+}
+
+macro_rules! todo {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::todo!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::todo!($($x)*);
+ }
+ };
+}
+
+macro_rules! unreachable {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::unreachable!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::unreachable!($($x)*);
+ }
+ };
+}
+
+macro_rules! panic {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::panic!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::panic!($($x)*);
+ }
+ };
+}
+
+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 ),*);
+ }
+ };
+}
+
+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 ),*);
+ }
+ };
+}
+
+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 ),*);
+ }
+ };
+}
+
+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 ),*);
+ }
+ };
+}
+
+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")]
+macro_rules! unwrap {
+ ($($x:tt)*) => {
+ ::defmt::unwrap!($($x)*)
+ };
+}
+
+#[cfg(not(feature = "defmt"))]
+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);
+ }
+ }
+ }
+}
+
+#[cfg(feature = "defmt-timestamp-uptime")]
+defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() }
+
+#[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
+ }
+}
diff --git a/embassy-futures/src/lib.rs b/embassy-futures/src/lib.rs
new file mode 100644
index 00000000..48c9c857
--- /dev/null
+++ b/embassy-futures/src/lib.rs
@@ -0,0 +1,12 @@
+#![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 select;
+mod yield_now;
+
+pub use select::*;
+pub use yield_now::*;
diff --git a/embassy-futures/src/select.rs b/embassy-futures/src/select.rs
new file mode 100644
index 00000000..8cecb7fa
--- /dev/null
+++ b/embassy-futures/src/select.rs
@@ -0,0 +1,230 @@
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+/// Result for [`select`].
+#[derive(Debug, Clone)]
+pub enum Either<A, B> {
+ /// First future finished first.
+ First(A),
+ /// Second future finished first.
+ Second(B),
+}
+
+/// 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)]
+pub enum Either3<A, B, C> {
+ /// First future finished first.
+ First(A),
+ /// Second future finished first.
+ Second(B),
+ /// Third future finished first.
+ Third(C),
+}
+
+/// 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)]
+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),
+}
+
+/// 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
+ }
+}
+
+// ====================================================================
+
+/// Future for the [`select_all`] function.
+#[derive(Debug)]
+#[must_use = "futures do nothing unless you `.await` or poll them"]
+pub struct SelectAll<Fut, const N: usize> {
+ inner: [Fut; N],
+}
+
+/// Creates a new future which will select over a list of futures.
+///
+/// The returned future will wait for any future within `iter` to be ready. Upon
+/// completion the item resolved will be returned, along with the index of the
+/// future that was ready.
+///
+/// # Panics
+///
+/// This function will panic if the array specified contains no items.
+pub fn select_all<Fut: Future, const N: usize>(arr: [Fut; N]) -> SelectAll<Fut, N> {
+ assert!(N > 0);
+ SelectAll { inner: arr }
+}
+
+impl<Fut: Future, const N: usize> Future for SelectAll<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,
+ }
+ }
+}
diff --git a/embassy-futures/src/yield_now.rs b/embassy-futures/src/yield_now.rs
new file mode 100644
index 00000000..1ebecb91
--- /dev/null
+++ b/embassy-futures/src/yield_now.rs
@@ -0,0 +1,25 @@
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+/// Yield from the current task once, allowing other tasks to run.
+pub fn yield_now() -> impl Future<Output = ()> {
+ YieldNowFuture { yielded: false }
+}
+
+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
+ }
+ }
+}