Skip to content

Support for wasm32-unknown-unknown #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Jan 27, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
abf64b7
Support wasm
expenses Dec 14, 2019
2f1984a
Error instead
expenses Dec 14, 2019
17b80a1
Revert "Error instead"
expenses Dec 14, 2019
10722a7
add wasm global
expenses Dec 15, 2019
979bae8
src/global.rs -> src/global_native.rs
expenses Dec 15, 2019
0ca73c2
Fix tests, cargo fmt
expenses Dec 15, 2019
11baa0e
Switch to wasm-timer for Instant, don't use parking_lot
expenses Dec 17, 2019
e394596
Fmt
expenses Dec 17, 2019
bfbe40d
Use feature flag instead
expenses Jan 2, 2020
832cfa2
Merge remote-tracking branch 'origin/master' into wasm-support
expenses Jan 2, 2020
0b5fe5e
Update Cargo.toml
expenses Jan 2, 2020
00d43f3
Merge branch 'wasm-support' of github.com:expenses/futures-timer into…
expenses Jan 2, 2020
e8e60aa
Revert "Update Cargo.toml"
expenses Jan 2, 2020
ab267cc
Fix native build
expenses Jan 7, 2020
94cb276
Use global_native when not on wasm32
expenses Jan 7, 2020
db64f45
Appease clippy a bit
expenses Jan 8, 2020
ae4a837
Switch to gloo-timers
expenses Jan 15, 2020
2250577
Fix pinning
expenses Jan 15, 2020
5ced38b
Add send_wrapper
expenses Jan 15, 2020
1c66b18
Add a reset stub
expenses Jan 15, 2020
2746e4d
Change reset behavious to panic
expenses Jan 15, 2020
5d737ca
Remove 'when' from Delay, switch reset to take a Duration
expenses Jan 21, 2020
4cf9125
run cargo fmt
expenses Jan 21, 2020
034ea15
Update src/delay_wasm.rs
expenses Jan 27, 2020
0190692
Update src/delay_wasm.rs
expenses Jan 27, 2020
511b0a1
Update src/delay.rs
expenses Jan 27, 2020
ef8a441
fix clippy, nitpicks
expenses Jan 27, 2020
06d1bd8
Merge branch 'wasm-support' of github.com:expenses/futures-timer into…
expenses Jan 27, 2020
9f620d7
delay -> dur
expenses Jan 27, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ Timeouts for futures.

[dependencies]

[target.wasm32-unknown-unknown.dependencies]
instant = { version = "0.1.2", features = ["wasm-bindgen"] }
wasm-bindgen = "0.2.55"
futures = "0.3.1"
web-sys = "0.3.32"
parking_lot = "0.10.0"

[dev-dependencies]
async-std = { version = "1.0.1", features = ["attributes"] }
futures = "0.3.1"
3 changes: 2 additions & 1 deletion src/delay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
//! This module contains the `Delay` type which is a future that will resolve
//! at a particular point in the future.

use crate::Instant;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use std::time::{Duration, Instant};
use std::time::Duration;

use crate::arc_list::Node;
use crate::AtomicWaker;
Expand Down
2 changes: 1 addition & 1 deletion src/global.rs → src/global_native.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::Instant;
use std::future::Future;
use std::io;
use std::mem::{self, ManuallyDrop};
Expand All @@ -7,7 +8,6 @@ use std::sync::Arc;
use std::task::{Context, RawWaker, RawWakerVTable, Waker};
use std::thread;
use std::thread::Thread;
use std::time::Instant;

use crate::{Timer, TimerHandle};

Expand Down
82 changes: 82 additions & 0 deletions src/global_wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use futures::task::{self, ArcWake};
use parking_lot::Mutex;
use std::convert::TryFrom;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::Context;
use std::time::Duration;
use wasm_bindgen::{closure::Closure, JsCast};

use crate::{Instant, Timer, TimerHandle};

/// Starts a background task, creates a `Timer`, and returns a handle to it.
///
/// > **Note**: Contrary to the native implementation, we don't have
/// > any `forget()` method, as the task is automatically considered
/// > as "forgotten".
pub(crate) fn run() -> TimerHandle {
let timer = Timer::new();
let handle = timer.handle();
schedule_callback(Arc::new(Mutex::new(timer)), Duration::new(0, 0));
handle
}

/// Calls `Window::setTimeout` with the given `Duration`. The callback wakes up the timer and
/// processes everything.
fn schedule_callback(timer: Arc<Mutex<Timer>>, when: Duration) {
let window = web_sys::window().expect("Unable to access Window");
let _ = window
.set_timeout_with_callback_and_timeout_and_arguments_0(
&Closure::once_into_js(move || {
let mut timer_lock = timer.lock();

// We start by polling the timer. If any new `Delay` is created, the waker will be used
// to wake up this task pre-emptively. As such, we pass a `Waker` that calls
// `schedule_callback` with a delay of `0`.
let waker = task::waker(Arc::new(Waker {
timer: timer.clone(),
}));
let _ = Future::poll(Pin::new(&mut *timer_lock), &mut Context::from_waker(&waker));

// Notify the timers that are ready.
let now = Instant::now();
timer_lock.advance_to(now);

// Each call to `schedule_callback` calls `schedule_callback` again, but also leaves
// the possibility for `schedule_callback` to be called in parallel. Since we don't
// want too many useless callbacks, we...
// TODO: ugh, that's a hack
if Arc::strong_count(&timer) > 20 {
return;
}

// We call `schedule_callback` again for the next event.
let sleep_dur = timer_lock
.next_event()
.map(|next_event| {
if next_event > now {
next_event - now
} else {
Duration::new(0, 0)
}
})
.unwrap_or(Duration::from_secs(5));
drop(timer_lock);
schedule_callback(timer, sleep_dur);
})
.unchecked_ref(),
i32::try_from(when.as_millis()).unwrap_or(0),
)
.unwrap();
}

struct Waker {
timer: Arc<Mutex<Timer>>,
}

impl ArcWake for Waker {
fn wake_by_ref(arc_self: &Arc<Self>) {
schedule_callback(arc_self.timer.clone(), Duration::new(0, 0));
}
}
2 changes: 1 addition & 1 deletion src/heap_timer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::Instant;
use std::cmp::Ordering;
use std::sync::Arc;
use std::time::Instant;

use crate::{Node, ScheduledTimer};

Expand Down
9 changes: 8 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
mod arc_list;
mod atomic_waker;
mod delay;
mod global;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
mod global_native;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
mod global_wasm;
mod heap;
mod heap_timer;
mod timer;
Expand All @@ -31,3 +34,7 @@ use heap_timer::HeapTimer;
use timer::{ScheduledTimer, Timer, TimerHandle};

pub use self::delay::Delay;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use instant::Instant;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use std::time::Instant;
51 changes: 32 additions & 19 deletions src/timer.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use crate::Instant;
use std::fmt;
use std::future::Future;
use std::mem;
use std::pin::Pin;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::sync::{Arc, Mutex, Weak};
use std::task::{Context, Poll};
use std::time::Instant;

use std::future::Future;

use crate::AtomicWaker;
use crate::{global, ArcList, Heap, HeapTimer, Node, Slot};
use crate::{ArcList, Heap, HeapTimer, Node, Slot};

/// A "timer heap" used to power separately owned instances of `Delay`.
///
Expand Down Expand Up @@ -278,23 +277,37 @@ impl Default for TimerHandle {
// handle which will return errors when timer objects are attempted to
// be associated.
if fallback == 0 {
let helper = match global::HelperThread::new() {
Ok(helper) => helper,
Err(_) => return TimerHandle { inner: Weak::new() },
};
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
{
let helper = match crate::global_native::HelperThread::new() {
Ok(helper) => helper,
Err(_) => return TimerHandle { inner: Weak::new() },
};

// If we successfully set ourselves as the actual fallback then we
// want to `forget` the helper thread to ensure that it persists
// globally. If we fail to set ourselves as the fallback that means
// that someone was racing with this call to
// `TimerHandle::default`. They ended up winning so we'll destroy
// our helper thread (which shuts down the thread) and reload the
// fallback.
if helper.handle().set_as_global_fallback().is_ok() {
let ret = helper.handle();
helper.forget();
return ret;
}
}

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
{
let handle = crate::global_wasm::run();

// If we successfully set ourselves as the actual fallback then we
// want to `forget` the helper thread to ensure that it persists
// globally. If we fail to set ourselves as the fallback that means
// that someone was racing with this call to
// `TimerHandle::default`. They ended up winning so we'll destroy
// our helper thread (which shuts down the thread) and reload the
// fallback.
if helper.handle().set_as_global_fallback().is_ok() {
let ret = helper.handle();
helper.forget();
return ret;
// Same as above.
if handle.clone().set_as_global_fallback().is_ok() {
return handle;
}
}

fallback = HANDLE_FALLBACK.load(SeqCst);
}

Expand Down