use std::{ future::Future, pin::Pin, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, task::{Context, Poll, Wake, Waker}, }; pub enum Either { Left(L), Right(R), } pub fn select(left: L, right: R) -> impl Future> + Unpin where L: Future + Unpin, R: Future + Unpin, { Select { left: SelectState { woken: Arc::new(AtomicBool::new(true)), future: left, }, right: SelectState { woken: Arc::new(AtomicBool::new(true)), future: right, }, } } struct SelectWaker { woken: Arc, parent: Waker, } struct SelectState { woken: Arc, future: T, } struct Select { left: SelectState, right: SelectState, } impl SelectState { fn check_for_poll(&self) -> bool { self.woken.swap(false, Ordering::AcqRel) } } impl Wake for SelectWaker { fn wake(self: Arc) { self.wake_by_ref(); } fn wake_by_ref(self: &Arc) { self.woken.store(true, Ordering::Release); self.parent.wake_by_ref(); } } impl Future for Select where L: Future + Unpin, R: Future + Unpin, { type Output = Either; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); if this.left.check_for_poll() { if let Poll::Ready(out) = Pin::new(&mut this.left.future).poll(cx) { return Poll::Ready(Either::Left(out)); } } if this.right.check_for_poll() { if let Poll::Ready(out) = Pin::new(&mut this.right.future).poll(cx) { return Poll::Ready(Either::Right(out)); } } Poll::Pending } }