use clonelet::clone;
use futures::{Future, FutureExt, StreamExt};
use futures_signals::{
signal::{Mutable, ReadOnlyMutable, Signal, SignalExt},
signal_vec::{MutableVec, MutableVecLockMut, SignalVec, SignalVecExt, VecDiff},
};
use silkenweb_macros::cfg_browser;
#[cfg_browser(false)]
pub mod server {
use std::{
pin::pin,
sync::Arc,
task::{Context, Poll, Wake},
};
use crossbeam::sync::{Parker, Unparker};
use futures::Future;
pub fn run_tasks_sync() {
super::arch::run_tasks_sync()
}
struct ThreadWaker(Unparker);
impl Wake for ThreadWaker {
fn wake(self: Arc<Self>) {
self.0.unpark();
}
}
pub fn block_on<T>(fut: impl Future<Output = T>) -> T {
let mut fut = pin!(fut);
let parker = Parker::new();
let waker = Arc::new(ThreadWaker(parker.unparker().clone())).into();
let mut cx = Context::from_waker(&waker);
loop {
match fut.as_mut().poll(&mut cx) {
Poll::Ready(res) => return res,
Poll::Pending => parker.park(),
}
}
}
}
#[cfg_browser(false)]
mod arch {
use std::{cell::RefCell, future::Future};
use futures::{
executor::{LocalPool, LocalSpawner},
task::LocalSpawnExt,
};
use tokio::task_local;
pub struct Runtime {
executor: RefCell<LocalPool>,
spawner: LocalSpawner,
}
impl Default for Runtime {
fn default() -> Self {
let executor = RefCell::new(LocalPool::new());
let spawner = executor.borrow().spawner();
Self { executor, spawner }
}
}
task_local! {
pub static RUNTIME: Runtime;
}
fn with_runtime<R>(f: impl FnOnce(&Runtime) -> R) -> R {
match RUNTIME.try_with(f) {
Ok(r) => r,
Err(_) => panic!("Must be run from within `silkenweb_task::task::scope`"),
}
}
pub fn scope<Fut>(f: Fut) -> impl Future<Output = Fut::Output>
where
Fut: Future,
{
RUNTIME.scope(Runtime::default(), f)
}
pub fn sync_scope<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
RUNTIME.sync_scope(Runtime::default(), f)
}
pub async fn run_tasks() {
run_tasks_sync()
}
pub fn run_tasks_sync() {
with_runtime(|runtime| runtime.executor.borrow_mut().run_until_stalled())
}
pub fn spawn_local<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
with_runtime(|runtime| runtime.spawner.spawn_local(future).unwrap())
}
}
#[cfg_browser(true)]
mod arch {
use std::future::Future;
use js_sys::Promise;
use wasm_bindgen::{JsValue, UnwrapThrowExt};
use wasm_bindgen_futures::JsFuture;
pub fn scope<Fut>(f: Fut) -> impl Future<Output = Fut::Output>
where
Fut: Future,
{
f
}
pub fn sync_scope<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
f()
}
pub async fn run_tasks() {
let wait_for_microtasks = Promise::resolve(&JsValue::NULL);
JsFuture::from(wait_for_microtasks).await.unwrap_throw();
}
pub fn spawn_local<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
wasm_bindgen_futures::spawn_local(future)
}
}
pub async fn run_tasks() {
arch::run_tasks().await
}
pub use arch::scope;
pub use arch::sync_scope;
pub fn spawn_local<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
arch::spawn_local(future)
}
pub trait TaskSignal: Signal {
fn to_mutable(self) -> ReadOnlyMutable<Self::Item>;
fn spawn_for_each<U, F>(self, callback: F)
where
U: Future<Output = ()> + 'static,
F: FnMut(Self::Item) -> U + 'static;
}
impl<Sig> TaskSignal for Sig
where
Sig: Signal + 'static,
{
fn to_mutable(self) -> ReadOnlyMutable<Self::Item> {
let mut s = Box::pin(self.to_stream());
let first_value = s
.next()
.now_or_never()
.expect("A `Signal`'s initial value must be `Ready` immediately")
.expect("`Signal`s must have an initial value");
let mutable = Mutable::new(first_value);
spawn_local({
clone!(mutable);
async move {
while let Some(value) = s.next().await {
mutable.set(value);
}
}
});
mutable.read_only()
}
fn spawn_for_each<U, F>(self, callback: F)
where
U: Future<Output = ()> + 'static,
F: FnMut(Self::Item) -> U + 'static,
{
spawn_local(self.for_each(callback));
}
}
pub trait TaskSignalVec: SignalVec {
fn to_mutable(self) -> MutableVec<Self::Item>;
fn spawn_for_each<U, F>(self, callback: F)
where
U: Future<Output = ()> + 'static,
F: FnMut(VecDiff<Self::Item>) -> U + 'static;
}
impl<Sig> TaskSignalVec for Sig
where
Self::Item: Clone + 'static,
Sig: SignalVec + 'static,
{
fn to_mutable(self) -> MutableVec<Self::Item> {
let mv = MutableVec::new();
self.spawn_for_each({
clone!(mv);
move |diff| {
MutableVecLockMut::apply_vec_diff(&mut mv.lock_mut(), diff);
async {}
}
});
mv
}
fn spawn_for_each<U, F>(self, callback: F)
where
U: Future<Output = ()> + 'static,
F: FnMut(VecDiff<Self::Item>) -> U + 'static,
{
spawn_local(self.for_each(callback));
}
}