extern crate alloc;
use super::{BroadcastWakerError, BroadcastWakerSubscriptionId, BroadcastWakerSubscriptions};
use crate::sync_types::{self, Lock as _, SyncRcPtrRef};
use core::{cell, convert, future, marker, pin, sync::atomic, task};
#[derive(Clone, Copy, Debug)]
pub enum BroadcastFutureError {
MemoryAllocationFailure,
}
impl convert::From<BroadcastWakerError> for BroadcastFutureError {
fn from(value: BroadcastWakerError) -> Self {
match value {
BroadcastWakerError::MemoryAllocationFailure => BroadcastFutureError::MemoryAllocationFailure,
}
}
}
pub trait BroadcastedFuture: marker::Send {
type Output: Clone + marker::Send;
type AuxPollData<'a>;
fn poll<'a>(
self: pin::Pin<&mut Self>,
aux_data: &mut Self::AuxPollData<'a>,
cx: &mut task::Context<'_>,
) -> task::Poll<Self::Output>;
}
pub struct BroadcastFuture<ST: sync_types::SyncTypes, F: BroadcastedFuture> {
subscriptions: BroadcastWakerSubscriptions<ST>,
polling_state: ST::Lock<BroadcastFuturePollingState>,
inner_fut: cell::UnsafeCell<BroadcastFutureInnerFuture<F>>,
}
unsafe impl<ST: sync_types::SyncTypes, F: BroadcastedFuture> marker::Send for BroadcastFuture<ST, F> {}
unsafe impl<ST: sync_types::SyncTypes, F: BroadcastedFuture> marker::Sync for BroadcastFuture<ST, F> {}
impl<ST: sync_types::SyncTypes, F: BroadcastedFuture> BroadcastFuture<ST, F> {
pub fn new(inner: F) -> Self {
Self {
subscriptions: BroadcastWakerSubscriptions::new(),
polling_state: ST::Lock::from(BroadcastFuturePollingState::Idle),
inner_fut: cell::UnsafeCell::new(BroadcastFutureInnerFuture::Pending { inner }),
}
}
pub fn into_inner(self) -> Option<F> {
match self.inner_fut.into_inner() {
BroadcastFutureInnerFuture::Pending { inner } => Some(inner),
BroadcastFutureInnerFuture::Ready(_) => None,
}
}
pub fn subscribe<'a, BP: 'a + sync_types::SyncRcPtr<Self>, BR: sync_types::SyncRcPtrRef<'a, Self, BP>>(
this: pin::Pin<BR>,
) -> Result<BroadcastFutureSubscription<ST, F, BP>, BroadcastFutureError> {
let subscription_id = this.subscriptions.subscribe()?;
let broadcast_future = this.make_clone();
Ok(BroadcastFutureSubscription::new(broadcast_future, subscription_id))
}
fn cancel_subscription(&self, subscription_id: BroadcastWakerSubscriptionId) {
self.subscriptions.unsubscribe(subscription_id, false);
}
fn poll_from_subscription<
'a,
'b,
BP: 'a + sync_types::SyncRcPtr<Self>,
BR: 'a + sync_types::SyncRcPtrRef<'a, Self, BP>,
>(
this: pin::Pin<BR>,
subscription_id: BroadcastWakerSubscriptionId,
aux_poll_data: &mut F::AuxPollData<'b>,
cx: &mut task::Context<'_>,
) -> task::Poll<F::Output>
where
Self: 'a,
{
let this = unsafe { pin::Pin::into_inner_unchecked(this) };
let mut polling_state_guard = this.polling_state.lock();
let mut wake_gen = this.subscriptions.wake_gen();
if *polling_state_guard == BroadcastFuturePollingState::InPoll {
this.subscriptions
.set_subscription_waker(subscription_id, cx.waker().clone());
return task::Poll::Pending;
}
atomic::compiler_fence(atomic::Ordering::Acquire);
let inner_fut = this.inner_fut.get();
let inner_fut = unsafe { &mut *inner_fut };
let f = match inner_fut {
BroadcastFutureInnerFuture::Pending { inner } => inner,
BroadcastFutureInnerFuture::Ready(result) => {
drop(polling_state_guard);
this.subscriptions.unsubscribe(subscription_id, false);
return task::Poll::Ready(result.clone());
}
};
let waker = BroadcastWakerSubscriptions::waker(&sync_types::SyncRcPtrRefForInner::<
'_,
_,
_,
_,
BroadcastFutureDerefInnerSubscriptionsTag,
>::new(&this));
let mut task_waker_updated = false;
loop {
let in_poll_guard = BroadcastFutureInPollGuard::new(&this, polling_state_guard);
let f = unsafe { pin::Pin::new_unchecked(&mut *f) };
let result = BroadcastedFuture::poll(f, aux_poll_data, &mut task::Context::from_waker(&waker));
match result {
task::Poll::Ready(result) => {
*inner_fut = BroadcastFutureInnerFuture::Ready(result.clone());
polling_state_guard = in_poll_guard.release();
drop(polling_state_guard);
this.subscriptions.unsubscribe(subscription_id, true);
return task::Poll::Ready(result);
}
task::Poll::Pending => {
if !task_waker_updated {
this.subscriptions
.set_subscription_waker(subscription_id, cx.waker().clone());
task_waker_updated = true;
}
polling_state_guard = in_poll_guard.release();
let cur_wake_gen = this.subscriptions.wake_gen();
if wake_gen != cur_wake_gen {
wake_gen = cur_wake_gen;
} else {
return task::Poll::Pending;
}
}
}
}
}
}
struct BroadcastFutureDerefInnerSubscriptionsTag;
impl<ST: sync_types::SyncTypes, F: BroadcastedFuture>
sync_types::DerefInnerByTag<BroadcastFutureDerefInnerSubscriptionsTag> for BroadcastFuture<ST, F>
{
crate::impl_deref_inner_by_tag!(subscriptions, BroadcastWakerSubscriptions<ST>);
}
impl<ST: sync_types::SyncTypes, F: BroadcastedFuture>
sync_types::DerefMutInnerByTag<BroadcastFutureDerefInnerSubscriptionsTag> for BroadcastFuture<ST, F>
{
crate::impl_deref_mut_inner_by_tag!(subscriptions);
}
enum BroadcastFutureInnerFuture<F: BroadcastedFuture> {
Pending {
inner: F,
},
Ready(F::Output),
}
#[derive(PartialEq, Eq, Debug)]
enum BroadcastFuturePollingState {
Idle,
InPoll,
}
struct BroadcastFutureInPollGuard<'a, ST: sync_types::SyncTypes, F: BroadcastedFuture> {
broadcast_future: &'a BroadcastFuture<ST, F>,
locked_in_poll: bool,
}
impl<'a, ST: sync_types::SyncTypes, F: BroadcastedFuture> BroadcastFutureInPollGuard<'a, ST, F> {
fn new<'b>(
broadcast_future: &'a BroadcastFuture<ST, F>,
mut polling_state_guard: <ST::Lock<BroadcastFuturePollingState> as sync_types::Lock<
BroadcastFuturePollingState,
>>::Guard<'b>,
) -> Self {
debug_assert_eq!(*polling_state_guard, BroadcastFuturePollingState::Idle);
*polling_state_guard = BroadcastFuturePollingState::InPoll;
Self {
broadcast_future,
locked_in_poll: true,
}
}
fn release(
mut self,
) -> <ST::Lock<BroadcastFuturePollingState> as sync_types::Lock<BroadcastFuturePollingState>>::Guard<'a> {
let mut polling_state_guard = self.broadcast_future.polling_state.lock();
*polling_state_guard = BroadcastFuturePollingState::Idle;
self.locked_in_poll = false;
polling_state_guard
}
}
impl<'a, ST: sync_types::SyncTypes, F: BroadcastedFuture> Drop for BroadcastFutureInPollGuard<'a, ST, F> {
fn drop(&mut self) {
if self.locked_in_poll {
*self.broadcast_future.polling_state.lock() = BroadcastFuturePollingState::Idle;
self.locked_in_poll = false;
}
}
}
pub struct BroadcastFutureSubscription<
ST: sync_types::SyncTypes,
F: BroadcastedFuture,
BP: sync_types::SyncRcPtr<BroadcastFuture<ST, F>>,
> {
state: BroadcastFutureSubscriptionState<ST, F, BP>,
}
impl<ST: sync_types::SyncTypes, F: BroadcastedFuture, BP: sync_types::SyncRcPtr<BroadcastFuture<ST, F>>>
BroadcastFutureSubscription<ST, F, BP>
{
fn new(broadcast_future: pin::Pin<BP>, subscription_id: BroadcastWakerSubscriptionId) -> Self {
Self {
state: BroadcastFutureSubscriptionState::Pending {
broadcast_future,
subscription_id,
_phantom: marker::PhantomData,
},
}
}
pub fn poll<'a>(
self: pin::Pin<&mut Self>,
aux_poll_data: &mut F::AuxPollData<'a>,
cx: &mut task::Context<'_>,
) -> task::Poll<F::Output> {
let this = self.get_mut();
match &this.state {
BroadcastFutureSubscriptionState::Pending {
broadcast_future,
subscription_id,
_phantom,
} => {
let result = BroadcastFuture::poll_from_subscription(
sync_types::SyncRcPtr::as_ref(broadcast_future),
*subscription_id,
aux_poll_data,
cx,
);
if matches!(result, task::Poll::Ready(_)) {
this.state = BroadcastFutureSubscriptionState::Done;
}
result
}
BroadcastFutureSubscriptionState::Done => unreachable!(),
}
}
}
impl<ST: sync_types::SyncTypes, F: BroadcastedFuture, BP: sync_types::SyncRcPtr<BroadcastFuture<ST, F>>> Drop
for BroadcastFutureSubscription<ST, F, BP>
{
fn drop(&mut self) {
match &self.state {
BroadcastFutureSubscriptionState::Pending {
broadcast_future,
subscription_id,
_phantom,
} => {
broadcast_future.cancel_subscription(*subscription_id);
self.state = BroadcastFutureSubscriptionState::Done;
}
BroadcastFutureSubscriptionState::Done => (),
}
}
}
impl<
'a,
ST: sync_types::SyncTypes,
F: BroadcastedFuture<AuxPollData<'a> = ()>,
BP: sync_types::SyncRcPtr<BroadcastFuture<ST, F>>,
> future::Future for BroadcastFutureSubscription<ST, F, BP>
{
type Output = F::Output;
fn poll(self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
BroadcastFutureSubscription::poll(self, &mut (), cx)
}
}
enum BroadcastFutureSubscriptionState<
ST: sync_types::SyncTypes,
F: BroadcastedFuture,
BP: sync_types::SyncRcPtr<BroadcastFuture<ST, F>>,
> {
Pending {
broadcast_future: pin::Pin<BP>,
subscription_id: BroadcastWakerSubscriptionId,
_phantom: marker::PhantomData<fn() -> (*const ST, *const F)>,
},
Done,
}
#[test]
fn test_broadcast_future_single() {
use crate::test::{TestAsyncExecutor, TestNopSyncTypes};
struct TestBroadcastedFuture {}
impl BroadcastedFuture for TestBroadcastedFuture {
type Output = u32;
type AuxPollData<'a> = ();
fn poll<'a>(
self: pin::Pin<&mut Self>,
_aux_data: &mut Self::AuxPollData<'a>,
_cx: &mut task::Context<'_>,
) -> task::Poll<Self::Output> {
task::Poll::Ready(1u32)
}
}
let broadcast_future =
<<TestNopSyncTypes as sync_types::SyncTypes>::SyncRcPtrFactory as sync_types::SyncRcPtrFactory>::try_new(
BroadcastFuture::<TestNopSyncTypes, TestBroadcastedFuture>::new(TestBroadcastedFuture {}),
)
.unwrap();
let broadcast_future = unsafe { pin::Pin::new_unchecked(broadcast_future) };
let subscription = BroadcastFuture::subscribe(sync_types::SyncRcPtr::as_ref(&broadcast_future)).unwrap();
let e = TestAsyncExecutor::new();
let w = TestAsyncExecutor::spawn(&e, subscription);
TestAsyncExecutor::run_to_completion(&e);
assert_eq!(w.take().unwrap(), 1u32);
}
#[test]
fn test_broadcast_future_broadcast() {
use crate::test::{TestAsyncExecutor, TestNopSyncTypes};
struct TestBroadcastedFuture {
polled_once: bool,
}
impl BroadcastedFuture for TestBroadcastedFuture {
type Output = u32;
type AuxPollData<'a> = ();
fn poll<'a>(
self: pin::Pin<&mut Self>,
_aux_data: &mut Self::AuxPollData<'a>,
_cx: &mut task::Context<'_>,
) -> task::Poll<Self::Output> {
if !self.polled_once {
self.get_mut().polled_once = true;
task::Poll::Pending
} else {
task::Poll::Ready(1u32)
}
}
}
let broadcast_future =
<<TestNopSyncTypes as sync_types::SyncTypes>::SyncRcPtrFactory as sync_types::SyncRcPtrFactory>::try_new(
BroadcastFuture::<TestNopSyncTypes, TestBroadcastedFuture>::new(TestBroadcastedFuture {
polled_once: false,
}),
)
.unwrap();
let broadcast_future = unsafe { pin::Pin::new_unchecked(broadcast_future) };
let subscription0 = BroadcastFuture::subscribe(sync_types::SyncRcPtr::as_ref(&broadcast_future)).unwrap();
let subscription1 = BroadcastFuture::subscribe(sync_types::SyncRcPtr::as_ref(&broadcast_future)).unwrap();
let e = TestAsyncExecutor::new();
let w0 = TestAsyncExecutor::spawn(&e, subscription0);
let w1 = TestAsyncExecutor::spawn(&e, subscription1);
TestAsyncExecutor::run_to_completion(&e);
assert_eq!(w0.take().unwrap(), 1u32);
assert_eq!(w1.take().unwrap(), 1u32);
}
#[test]
fn test_broadcast_future_post_completion_subscribe() {
use crate::test::{TestAsyncExecutor, TestNopSyncTypes};
struct TestBroadcastedFuture {
done: bool,
}
impl BroadcastedFuture for TestBroadcastedFuture {
type Output = u32;
type AuxPollData<'a> = ();
fn poll<'a>(
self: pin::Pin<&mut Self>,
_aux_data: &mut Self::AuxPollData<'a>,
_cx: &mut task::Context<'_>,
) -> task::Poll<Self::Output> {
assert!(!self.done);
self.get_mut().done = true;
task::Poll::Ready(1u32)
}
}
let broadcast_future =
<<TestNopSyncTypes as sync_types::SyncTypes>::SyncRcPtrFactory as sync_types::SyncRcPtrFactory>::try_new(
BroadcastFuture::<TestNopSyncTypes, TestBroadcastedFuture>::new(TestBroadcastedFuture { done: false }),
)
.unwrap();
let broadcast_future = unsafe { pin::Pin::new_unchecked(broadcast_future) };
let subscription0 = BroadcastFuture::subscribe(sync_types::SyncRcPtr::as_ref(&broadcast_future)).unwrap();
let e = TestAsyncExecutor::new();
let w0 = TestAsyncExecutor::spawn(&e, subscription0);
TestAsyncExecutor::run_to_completion(&e);
assert_eq!(w0.take().unwrap(), 1u32);
let subscription1 = BroadcastFuture::subscribe(sync_types::SyncRcPtr::as_ref(&broadcast_future)).unwrap();
let w1 = TestAsyncExecutor::spawn(&e, subscription1);
TestAsyncExecutor::run_to_completion(&e);
assert_eq!(w1.take().unwrap(), 1u32);
}
#[test]
fn test_broadcast_future_cancel_subscription() {
use crate::test::{TestAsyncExecutor, TestNopSyncTypes};
struct TestBroadcastedFuture {}
impl BroadcastedFuture for TestBroadcastedFuture {
type Output = u32;
type AuxPollData<'a> = ();
fn poll<'a>(
self: pin::Pin<&mut Self>,
_aux_data: &mut Self::AuxPollData<'a>,
_cx: &mut task::Context<'_>,
) -> task::Poll<Self::Output> {
task::Poll::Ready(1u32)
}
}
let broadcast_future =
<<TestNopSyncTypes as sync_types::SyncTypes>::SyncRcPtrFactory as sync_types::SyncRcPtrFactory>::try_new(
BroadcastFuture::<TestNopSyncTypes, TestBroadcastedFuture>::new(TestBroadcastedFuture {}),
)
.unwrap();
let broadcast_future = unsafe { pin::Pin::new_unchecked(broadcast_future) };
let subscription0 = BroadcastFuture::subscribe(sync_types::SyncRcPtr::as_ref(&broadcast_future)).unwrap();
let subscription1 = BroadcastFuture::subscribe(sync_types::SyncRcPtr::as_ref(&broadcast_future)).unwrap();
let e = TestAsyncExecutor::new();
let w1 = TestAsyncExecutor::spawn(&e, subscription1);
drop(subscription0);
TestAsyncExecutor::run_to_completion(&e);
assert_eq!(w1.take().unwrap(), 1u32);
}