Skip to content

Commit e2163ce

Browse files
committed
fix(wasm): Fix panic when Delay is reused before resetting
1 parent 07dbe53 commit e2163ce

File tree

5 files changed

+70
-21
lines changed

5 files changed

+70
-21
lines changed

.github/workflows/ci.yml

+20-11
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,26 @@ jobs:
2121
- name: cargo doc
2222
run: cargo doc --no-deps
2323

24+
test_wasm:
25+
name: Test (wasm)
26+
runs-on: ubuntu-latest
27+
strategy:
28+
matrix:
29+
rust:
30+
- stable
31+
steps:
32+
- uses: actions/checkout@master
33+
- name: Install Rust and add wasm target
34+
run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} && rustup target add wasm32-unknown-unknown
35+
- name: Install wasm-pack
36+
uses: taiki-e/cache-cargo-install-action@v1
37+
with:
38+
39+
- name: cargo test
40+
run: wasm-pack test --firefox --headless -- --features=wasm-bindgen
41+
- name: cargo doc
42+
run: cargo doc --no-deps --target=wasm32-unknown-unknown --features=wasm-bindgen
43+
2444
style:
2545
name: Style
2646
runs-on: ubuntu-latest
@@ -59,14 +79,3 @@ jobs:
5979
git -c user.name='ci' -c user.email='ci' commit -m 'Deploy futures-timer API documentation'
6080
git push -f -q https://git:${{ secrets.github_token }}@github.com/${{ github.repository }} HEAD:gh-pages
6181
if: github.event_name == 'push' && github.event.ref == 'refs/heads/master' && github.repository == 'async-rs/futures-timer'
62-
63-
check_wasm:
64-
name: Check Wasm
65-
needs: [test]
66-
runs-on: ubuntu-latest
67-
steps:
68-
- uses: actions/checkout@master
69-
- name: Install Rust and add wasm target
70-
run: rustup update stable && rustup target add wasm32-unknown-unknown
71-
- name: cargo check
72-
run: cargo check --target wasm32-unknown-unknown --features wasm-bindgen

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ send_wrapper = { version = "0.4.0", optional = true }
1818

1919
[dev-dependencies]
2020
async-std = { version = "1.0.1", features = ["attributes"] }
21+
cfg-if = "1.0.0"
2122
futures = "0.3.1"
23+
wasm-bindgen-test = "0.3.42"
24+
web-time = "1.1.0"
2225

2326
[features]
2427
wasm-bindgen = [

src/wasm.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ use std::{
1111

1212
/// A version of `Delay` that works on wasm.
1313
#[derive(Debug)]
14-
pub struct Delay(SendWrapper<TimeoutFuture>);
14+
pub struct Delay(Option<SendWrapper<TimeoutFuture>>);
1515

1616
impl Delay {
1717
/// Creates a new future which will fire at `dur` time into the future.
1818
#[inline]
1919
pub fn new(dur: Duration) -> Delay {
20-
Self(SendWrapper::new(TimeoutFuture::new(dur.as_millis() as u32)))
20+
Self(Some(SendWrapper::new(TimeoutFuture::new(
21+
dur.as_millis() as u32
22+
))))
2123
}
2224

2325
/// Resets the timeout.
@@ -30,7 +32,16 @@ impl Delay {
3032
impl Future for Delay {
3133
type Output = ();
3234

33-
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
34-
Pin::new(&mut *Pin::into_inner(self).0).poll(cx)
35+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
36+
match self.0.as_mut() {
37+
Some(delay) => match Pin::new(&mut **delay).poll(cx) {
38+
Poll::Pending => Poll::Pending,
39+
Poll::Ready(()) => {
40+
self.0.take();
41+
Poll::Ready(())
42+
}
43+
},
44+
None => Poll::Ready(()),
45+
}
3546
}
3647
}

tests/smoke.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
11
use std::error::Error;
22
use std::pin::Pin;
3-
use std::time::{Duration, Instant};
3+
use std::time::Duration;
44

5+
use futures::FutureExt;
56
use futures_timer::Delay;
67

7-
#[async_std::test]
8+
cfg_if::cfg_if! {
9+
if #[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))] {
10+
use wasm_bindgen_test::wasm_bindgen_test as async_test;
11+
use web_time::Instant;
12+
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
13+
} else {
14+
use std::time::Instant;
15+
use async_std::test as async_test;
16+
}
17+
}
18+
19+
#[async_test]
820
async fn works() {
921
let i = Instant::now();
1022
let dur = Duration::from_millis(100);
1123
let _d = Delay::new(dur).await;
1224
assert!(i.elapsed() > dur);
1325
}
1426

15-
#[async_std::test]
27+
#[async_test]
1628
async fn reset() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
1729
let i = Instant::now();
1830
let dur = Duration::from_millis(100);
@@ -21,6 +33,9 @@ async fn reset() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
2133
// Allow us to re-use a future
2234
Pin::new(&mut d).await;
2335

36+
// Reusing without resetting should return immediately
37+
Pin::new(&mut d).now_or_never().unwrap();
38+
2439
assert!(i.elapsed() > dur);
2540

2641
let i = Instant::now();

tests/timeout.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
use std::error::Error;
2-
use std::time::{Duration, Instant};
2+
use std::time::Duration;
33

44
use futures_timer::Delay;
55

6-
#[async_std::test]
6+
cfg_if::cfg_if! {
7+
if #[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))] {
8+
use wasm_bindgen_test::wasm_bindgen_test as async_test;
9+
use web_time::Instant;
10+
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
11+
} else {
12+
use std::time::Instant;
13+
use async_std::test as async_test;
14+
}
15+
}
16+
17+
#[async_test]
718
async fn smoke() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
819
let dur = Duration::from_millis(10);
920
let start = Instant::now();
@@ -12,7 +23,7 @@ async fn smoke() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
1223
Ok(())
1324
}
1425

15-
#[async_std::test]
26+
#[async_test]
1627
async fn two() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
1728
let dur = Duration::from_millis(10);
1829
Delay::new(dur).await;

0 commit comments

Comments
 (0)