mod arc_async_derived;
pub use arc_async_derived::*;
#[allow(clippy::module_inception)] mod async_derived;
mod future_impls;
mod inner;
use crate::{
graph::{AnySubscriber, Observer, WithObserver},
owner::Owner,
};
pub use async_derived::*;
pub use future_impls::*;
use futures::Future;
use pin_project_lite::pin_project;
use std::{
pin::Pin,
task::{Context, Poll},
};
pin_project! {
#[derive(Clone)]
#[allow(missing_docs)]
pub struct ScopedFuture<Fut> {
owner: Owner,
observer: Option<AnySubscriber>,
diagnostics: bool,
#[pin]
fut: Fut,
}
}
impl<Fut> ScopedFuture<Fut> {
pub fn new(fut: Fut) -> Self {
let owner = Owner::current().unwrap_or_default();
let observer = Observer::get();
Self {
owner,
observer,
diagnostics: true,
fut,
}
}
pub fn new_untracked(fut: Fut) -> Self {
let owner = Owner::current().unwrap_or_default();
Self {
owner,
observer: None,
diagnostics: false,
fut,
}
}
#[doc(hidden)]
#[track_caller]
pub fn new_untracked_with_diagnostics(fut: Fut) -> Self {
let owner = Owner::current().unwrap_or_default();
Self {
owner,
observer: None,
diagnostics: true,
fut,
}
}
}
impl<Fut: Future> Future for ScopedFuture<Fut> {
type Output = Fut::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
this.owner.with(|| {
this.observer.with_observer(|| {
#[cfg(debug_assertions)]
let _maybe_guard = if *this.diagnostics {
None
} else {
Some(crate::diagnostics::SpecialNonReactiveZone::enter())
};
this.fut.poll(cx)
})
})
}
}
pub mod suspense {
use crate::{
signal::ArcRwSignal,
traits::{ReadUntracked, Update, Write},
};
use futures::channel::oneshot::Sender;
use or_poisoned::OrPoisoned;
use slotmap::{DefaultKey, SlotMap};
use std::{
mem,
sync::{Arc, Mutex},
task::Waker,
};
#[derive(Clone, Debug)]
pub struct LocalResourceNotifier(Arc<Mutex<Option<Sender<()>>>>);
impl LocalResourceNotifier {
pub fn notify(&mut self) {
if let Some(tx) = self.0.lock().or_poisoned().take()
&& tx.send(()).is_err()
{
crate::log_warning(format_args!(
"A local-resource notification could not be delivered \
because its listener was already dropped."
));
}
}
}
impl From<Sender<()>> for LocalResourceNotifier {
fn from(value: Sender<()>) -> Self {
Self(Arc::new(Mutex::new(Some(value))))
}
}
#[derive(Clone, Debug)]
pub struct SuspenseContext {
pub tasks: ArcRwSignal<SlotMap<DefaultKey, ()>>,
empty_wakers: Arc<Mutex<Vec<Waker>>>,
}
impl SuspenseContext {
pub fn new(tasks: ArcRwSignal<SlotMap<DefaultKey, ()>>) -> Self {
Self {
tasks,
empty_wakers: Default::default(),
}
}
pub fn task_id(&self) -> TaskHandle {
let key = self.tasks.write().insert(());
TaskHandle {
tasks: self.tasks.clone(),
empty_wakers: Arc::clone(&self.empty_wakers),
key,
}
}
pub fn poll_empty(&self, waker: &Waker) -> bool {
let mut wakers = self.empty_wakers.lock().or_poisoned();
let empty = self
.tasks
.try_read_untracked()
.map(|tasks| tasks.is_empty())
.unwrap_or(false);
if empty {
wakers.clear();
} else if !wakers.iter().any(|w| w.will_wake(waker)) {
wakers.push(waker.clone());
}
empty
}
}
#[derive(Debug)]
pub struct TaskHandle {
tasks: ArcRwSignal<SlotMap<DefaultKey, ()>>,
empty_wakers: Arc<Mutex<Vec<Waker>>>,
key: DefaultKey,
}
impl Drop for TaskHandle {
fn drop(&mut self) {
let mut now_empty = false;
self.tasks.update(|tasks| {
tasks.remove(self.key);
now_empty = tasks.is_empty();
});
if now_empty {
for waker in
mem::take(&mut *self.empty_wakers.lock().or_poisoned())
{
waker.wake();
}
}
}
}
#[derive(Clone, Debug)]
pub struct SharedTaskHandle(Arc<Mutex<Option<TaskHandle>>>);
impl SharedTaskHandle {
pub fn new(handle: TaskHandle) -> Self {
Self(Arc::new(Mutex::new(Some(handle))))
}
pub fn release(&self) {
drop(self.0.lock().or_poisoned().take());
}
}
}