Skip to content

Commit

Permalink
Implement sleep and interval for Yew Platform (#2784)
Browse files Browse the repository at this point in the history
* Rename runtime.

* Implement sleep and interval.

* Replace sleep usage with one provided by the platform.

* Fix imports.

* Fix tests.

* Enable futures on gloo.

* Always inline sleep.

* Fix tests.

* Implement sleep in house.

* Remove futures feature.

* Oops.

* Prevent Excessive Polling.
  • Loading branch information
futursolo committed Jul 31, 2022
1 parent 4eda943 commit de613fa
Show file tree
Hide file tree
Showing 30 changed files with 224 additions and 95 deletions.
2 changes: 1 addition & 1 deletion packages/yew-router/tests/basename.rs
@@ -1,9 +1,9 @@
use std::time::Duration;

use gloo::timers::future::sleep;
use serde::{Deserialize, Serialize};
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use yew::functional::function_component;
use yew::platform::time::sleep;
use yew::prelude::*;
use yew_router::prelude::*;

Expand Down
2 changes: 1 addition & 1 deletion packages/yew-router/tests/browser_router.rs
@@ -1,9 +1,9 @@
use std::time::Duration;

use gloo::timers::future::sleep;
use serde::{Deserialize, Serialize};
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use yew::functional::function_component;
use yew::platform::time::sleep;
use yew::prelude::*;
use yew_router::prelude::*;

Expand Down
2 changes: 1 addition & 1 deletion packages/yew-router/tests/hash_router.rs
@@ -1,9 +1,9 @@
use std::time::Duration;

use gloo::timers::future::sleep;
use serde::{Deserialize, Serialize};
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use yew::functional::function_component;
use yew::platform::time::sleep;
use yew::prelude::*;
use yew_router::prelude::*;

Expand Down
2 changes: 1 addition & 1 deletion packages/yew-router/tests/link.rs
@@ -1,9 +1,9 @@
use std::time::Duration;

use gloo::timers::future::sleep;
use serde::{Deserialize, Serialize};
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use yew::functional::function_component;
use yew::platform::time::sleep;
use yew::prelude::*;
use yew_router::prelude::*;

Expand Down
6 changes: 3 additions & 3 deletions packages/yew/Cargo.toml
Expand Up @@ -25,7 +25,7 @@ slab = "0.4"
wasm-bindgen = "0.2"
yew-macro = { version = "^0.19.0", path = "../yew-macro" }
thiserror = "1.0"
futures = { version = "0.3", optional = true }
futures = { version = "0.3", default-features = false, features = ["std"] }
html-escape = { version = "0.2.9", optional = true }
implicit-clone = { version = "0.3", features = ["map"] }
base64ct = { version = "1.5.0", features = ["std"], optional = true }
Expand Down Expand Up @@ -93,8 +93,8 @@ features = [
]

[features]
tokio = ["tokio/rt", "dep:num_cpus", "dep:tokio-util"]
ssr = ["dep:futures", "dep:html-escape", "dep:base64ct", "dep:bincode"]
tokio = ["tokio/rt", "tokio/time", "dep:num_cpus", "dep:tokio-util"]
ssr = ["dep:html-escape", "dep:base64ct", "dep:bincode"]
csr = []
hydration = ["csr", "dep:bincode"]
nightly = ["yew-macro/nightly"]
Expand Down
6 changes: 2 additions & 4 deletions packages/yew/src/dom_bundle/bcomp.rs
Expand Up @@ -165,14 +165,12 @@ mod feat_hydration {
#[cfg(target_arch = "wasm32")]
#[cfg(test)]
mod tests {
use std::ops::Deref;

use gloo::utils::document;
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use web_sys::{Element, Node};
use web_sys::Element;

use super::*;
use crate::dom_bundle::{Bundle, Reconcilable, ReconcileTarget};
use crate::dom_bundle::{Reconcilable, ReconcileTarget};
use crate::virtual_dom::{Key, VChild, VNode};
use crate::{html, scheduler, Children, Component, Context, Html, NodeRef, Properties};

Expand Down
7 changes: 4 additions & 3 deletions packages/yew/src/platform/mod.rs
Expand Up @@ -46,15 +46,16 @@ use std::future::Future;
pub(crate) mod io;

pub mod sync;
pub mod time;

#[cfg(target_arch = "wasm32")]
#[path = "rt_wasm_bindgen.rs"]
#[path = "rt_wasm_bindgen/mod.rs"]
mod imp;
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
#[path = "rt_tokio.rs"]
#[path = "rt_tokio/mod.rs"]
mod imp;
#[cfg(all(not(target_arch = "wasm32"), not(feature = "tokio")))]
#[path = "rt_none.rs"]
#[path = "rt_none/mod.rs"]
mod imp;

/// Spawns a task on current thread.
Expand Down
26 changes: 0 additions & 26 deletions packages/yew/src/platform/rt_none.rs

This file was deleted.

30 changes: 30 additions & 0 deletions packages/yew/src/platform/rt_none/mod.rs
@@ -0,0 +1,30 @@
use std::future::Future;

pub(crate) mod time;

static NO_RUNTIME_NOTICE: &str = r#"No runtime configured for this platform, \
features that requires a runtime can't be used. \
Either compile with `target_arch = "wasm32", or enable the `tokio` feature."#;

fn panic_no_runtime() -> ! {
panic!("{}", NO_RUNTIME_NOTICE);
}

#[inline(always)]
pub(super) fn spawn_local<F>(_f: F)
where
F: Future<Output = ()> + 'static,
{
panic_no_runtime();
}

#[cfg(feature = "ssr")]
pub(crate) async fn run_pinned<F, Fut>(_create_task: F) -> Fut::Output
where
F: FnOnce() -> Fut,
F: Send + 'static,
Fut: Future + 'static,
Fut::Output: Send + 'static,
{
panic_no_runtime();
}
13 changes: 13 additions & 0 deletions packages/yew/src/platform/rt_none/time.rs
@@ -0,0 +1,13 @@
use std::time::Duration;

use futures::stream::LocalBoxStream;

use super::panic_no_runtime;

pub(crate) async fn sleep(_dur: Duration) {
panic_no_runtime();
}

pub(crate) fn interval(_dur: Duration) -> LocalBoxStream<'static, ()> {
panic_no_runtime();
}
@@ -1,5 +1,7 @@
use std::future::Future;

pub(crate) mod time;

#[cfg(feature = "ssr")]
pub(super) async fn run_pinned<F, Fut>(create_task: F) -> Fut::Output
where
Expand Down
14 changes: 14 additions & 0 deletions packages/yew/src/platform/rt_tokio/time.rs
@@ -0,0 +1,14 @@
use std::future::Future;
use std::time::Duration;

use futures::stream::{Stream, StreamExt};
use tokio_stream::wrappers::IntervalStream;

#[inline(always)]
pub(crate) fn sleep(dur: Duration) -> impl Future<Output = ()> {
tokio::time::sleep(dur)
}

pub(crate) fn interval(dur: Duration) -> impl Stream<Item = ()> {
IntervalStream::new(tokio::time::interval(dur)).then(|_| async {})
}
@@ -1,6 +1,8 @@
#[cfg(feature = "ssr")]
use std::future::Future;

pub(crate) mod time;

pub(super) use wasm_bindgen_futures::spawn_local;

#[cfg(feature = "ssr")]
Expand Down
75 changes: 75 additions & 0 deletions packages/yew/src/platform/rt_wasm_bindgen/time.rs
@@ -0,0 +1,75 @@
use std::cell::Cell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use std::time::Duration;

use futures::stream;
use futures::stream::Stream;
use gloo::timers::callback::Timeout;

#[inline(always)]
pub(crate) fn sleep(dur: Duration) -> impl Future<Output = ()> {
pub struct Sleep {
inner: Option<Timeout>,
dur_left: Option<u128>,
timeout_registered: Rc<Cell<bool>>,
}

impl Future for Sleep {
type Output = ();

fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
static I32_MAX_U128: u128 = 2_147_483_647;
static I32_MAX_U32: u32 = 2_147_483_647;

// If polling before the registered timeout is reached, return Pending.
if self.timeout_registered.get() {
return Poll::Pending;
}

// set_timeout can only accept maximum of i32, so we wrap around if it gets longer.
let next_timeout = match self.dur_left.map(|m| (m, u32::try_from(m))) {
Some((m_u128, Err(_))) => {
self.dur_left = Some(m_u128 - I32_MAX_U128);
I32_MAX_U32
}
Some((m_u128, _)) if m_u128 > I32_MAX_U128 => {
self.dur_left = Some(m_u128 - I32_MAX_U128);
I32_MAX_U32
}
Some((_, Ok(m_u32))) => {
self.dur_left = None;
m_u32
}
None => return Poll::Ready(()),
};

let waker = cx.waker().clone();
self.timeout_registered.set(true);
let timeout_registered = self.timeout_registered.clone();

self.inner = Some(Timeout::new(next_timeout, move || {
timeout_registered.set(false);
waker.wake();
}));

Poll::Pending
}
}

Sleep {
inner: None,
dur_left: Some(dur.as_millis()),
timeout_registered: Cell::new(false).into(),
}
}

pub(crate) fn interval(dur: Duration) -> impl Stream<Item = ()> {
stream::unfold((), move |_: ()| async move {
sleep(dur).await;

Some(((), ()))
})
}
20 changes: 20 additions & 0 deletions packages/yew/src/platform/time.rs
@@ -0,0 +1,20 @@
//! Utilities for bridging time and tasks.

use std::future::Future;
use std::time::Duration;

use futures::stream::Stream;

use crate::platform::imp::time as imp;

/// Waits until duration has elapsed.
#[inline(always)]
pub fn sleep(dur: Duration) -> impl Future<Output = ()> {
imp::sleep(dur)
}

/// Creates a Stream that yields an item after every period has elapsed.
#[inline(always)]
pub fn interval(period: Duration) -> impl Stream<Item = ()> {
imp::interval(period)
}
2 changes: 1 addition & 1 deletion packages/yew/src/virtual_dom/vsuspense.rs
Expand Up @@ -65,8 +65,8 @@ mod ssr_tests {

use tokio::task::{spawn_local, LocalSet};
use tokio::test;
use tokio::time::sleep;

use crate::platform::time::sleep;
use crate::prelude::*;
use crate::suspense::{Suspension, SuspensionResult};
use crate::ServerRenderer;
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/tests/common/mod.rs
Expand Up @@ -15,5 +15,5 @@ pub fn obtain_result_by_id(id: &str) -> String {
}

pub fn output_element() -> web_sys::Element {
gloo_utils::document().get_element_by_id("output").unwrap()
gloo::utils::document().get_element_by_id("output").unwrap()
}
4 changes: 2 additions & 2 deletions packages/yew/tests/hydration.rs
Expand Up @@ -8,11 +8,11 @@ use std::time::Duration;
mod common;

use common::{obtain_result, obtain_result_by_id};
use gloo::timers::future::sleep;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::spawn_local;
use wasm_bindgen_test::*;
use web_sys::{HtmlElement, HtmlTextAreaElement};
use yew::platform::time::sleep;
use yew::prelude::*;
use yew::suspense::{use_future, Suspension, SuspensionResult};
use yew::{Renderer, ServerRenderer};
Expand Down Expand Up @@ -769,7 +769,7 @@ async fn hydration_suspense_no_flickering() {
#[hook]
pub fn use_suspend() -> SuspensionResult<()> {
use_future(|| async {
gloo::timers::future::sleep(std::time::Duration::from_millis(200)).await;
yew::platform::time::sleep(std::time::Duration::from_millis(200)).await;
})?;
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/tests/layout.rs
Expand Up @@ -7,9 +7,9 @@ wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
use std::time::Duration;

use common::obtain_result;
use gloo::timers::future::sleep;
use wasm_bindgen_futures::spawn_local;
use wasm_bindgen_test::*;
use yew::platform::time::sleep;
use yew::prelude::*;

#[wasm_bindgen_test]
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/tests/mod.rs
Expand Up @@ -5,8 +5,8 @@ mod common;
use std::time::Duration;

use common::obtain_result;
use gloo::timers::future::sleep;
use wasm_bindgen_test::*;
use yew::platform::time::sleep;
use yew::prelude::*;

wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
Expand Down

0 comments on commit de613fa

Please sign in to comment.