Skip to content

Commit 45d2c7c

Browse files
committed
updated to the latest master
1 parent cbaa1d3 commit 45d2c7c

File tree

1 file changed

+51
-41
lines changed

1 file changed

+51
-41
lines changed

crates/futures/src/atomics.rs

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::cell::{Cell, RefCell};
22
use std::fmt;
3+
use std::rc::Rc;
34
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
45
use std::sync::Arc;
56

@@ -19,9 +20,7 @@ use wasm_bindgen::prelude::*;
1920
///
2021
/// Currently this type is constructed with `JsFuture::from`.
2122
pub struct JsFuture {
22-
resolved: oneshot::Receiver<JsValue>,
23-
rejected: oneshot::Receiver<JsValue>,
24-
callbacks: Option<(Closure<dyn FnMut(JsValue)>, Closure<dyn FnMut(JsValue)>)>,
23+
rx: oneshot::Receiver<Result<JsValue, JsValue>>,
2524
}
2625

2726
impl fmt::Debug for JsFuture {
@@ -33,28 +32,49 @@ impl fmt::Debug for JsFuture {
3332
impl From<Promise> for JsFuture {
3433
fn from(js: Promise) -> JsFuture {
3534
// Use the `then` method to schedule two callbacks, one for the
36-
// resolved value and one for the rejected value. These two callbacks
37-
// will be connected to oneshot channels which feed back into our
38-
// future.
35+
// resolved value and one for the rejected value. We're currently
36+
// assuming that JS engines will unconditionally invoke precisely one of
37+
// these callbacks, no matter what.
3938
//
40-
// This may not be the speediest option today but it should work!
41-
let (tx1, rx1) = oneshot::channel();
42-
let (tx2, rx2) = oneshot::channel();
43-
let mut tx1 = Some(tx1);
44-
let resolve = Closure::wrap(Box::new(move |val| {
45-
drop(tx1.take().unwrap().send(val));
46-
}) as Box<dyn FnMut(_)>);
47-
let mut tx2 = Some(tx2);
48-
let reject = Closure::wrap(Box::new(move |val| {
49-
drop(tx2.take().unwrap().send(val));
50-
}) as Box<dyn FnMut(_)>);
39+
// Ideally we'd have a way to cancel the callbacks getting invoked and
40+
// free up state ourselves when this `JsFuture` is dropped. We don't
41+
// have that, though, and one of the callbacks is likely always going to
42+
// be invoked.
43+
//
44+
// As a result we need to make sure that no matter when the callbacks
45+
// are invoked they are valid to be called at any time, which means they
46+
// have to be self-contained. Through the `Closure::once` and some
47+
// `Rc`-trickery we can arrange for both instances of `Closure`, and the
48+
// `Rc`, to all be destroyed once the first one is called.
49+
let (tx, rx) = oneshot::channel();
50+
let state = Rc::new(RefCell::new(None));
51+
let state2 = state.clone();
52+
let resolve = Closure::once(move |val| finish(&state2, Ok(val)));
53+
let state2 = state.clone();
54+
let reject = Closure::once(move |val| finish(&state2, Err(val)));
5155

5256
js.then2(&resolve, &reject);
53-
54-
JsFuture {
55-
resolved: rx1,
56-
rejected: rx2,
57-
callbacks: Some((resolve, reject)),
57+
*state.borrow_mut() = Some((tx, resolve, reject));
58+
59+
return JsFuture { rx };
60+
61+
fn finish(
62+
state: &RefCell<
63+
Option<(
64+
oneshot::Sender<Result<JsValue, JsValue>>,
65+
Closure<dyn FnMut(JsValue)>,
66+
Closure<dyn FnMut(JsValue)>,
67+
)>,
68+
>,
69+
val: Result<JsValue, JsValue>,
70+
) {
71+
match state.borrow_mut().take() {
72+
// We don't have any guarantee that anyone's still listening at this
73+
// point (the Rust `JsFuture` could have been dropped) so simply
74+
// ignore any errors here.
75+
Some((tx, _, _)) => drop(tx.send(val)),
76+
None => wasm_bindgen::throw_str("cannot finish twice"),
77+
}
5878
}
5979
}
6080
}
@@ -64,19 +84,11 @@ impl Future for JsFuture {
6484
type Error = JsValue;
6585

6686
fn poll(&mut self) -> Poll<JsValue, JsValue> {
67-
// Test if either our resolved or rejected side is finished yet. Note
68-
// that they will return errors if they're disconnected which can't
69-
// happen until we drop the `callbacks` field, which doesn't happen
70-
// till we're done, so we dont need to handle that.
71-
if let Ok(Async::Ready(val)) = self.resolved.poll() {
72-
drop(self.callbacks.take());
73-
return Ok(val.into());
87+
match self.rx.poll() {
88+
Ok(Async::Ready(val)) => val.map(Async::Ready),
89+
Ok(Async::NotReady) => Ok(Async::NotReady),
90+
Err(_) => wasm_bindgen::throw_str("cannot cancel"),
7491
}
75-
if let Ok(Async::Ready(val)) = self.rejected.poll() {
76-
drop(self.callbacks.take());
77-
return Err(val);
78-
}
79-
Ok(Async::NotReady)
8092
}
8193
}
8294

@@ -101,8 +113,8 @@ impl Future for JsFuture {
101113
/// resolve**. Instead it will be a leaked promise. This is an unfortunate
102114
/// limitation of wasm currently that's hoped to be fixed one day!
103115
pub fn future_to_promise<F>(future: F) -> Promise
104-
where
105-
F: Future<Item = JsValue, Error = JsValue> + 'static,
116+
where
117+
F: Future<Item = JsValue, Error = JsValue> + 'static,
106118
{
107119
_future_to_promise(Box::new(future))
108120
}
@@ -283,9 +295,7 @@ fn _future_to_promise(future: Box<dyn Future<Item = JsValue, Error = JsValue>>)
283295
break;
284296
}
285297

286-
State::Waiting(_) => {
287-
panic!("shouldn't see waiting state!")
288-
}
298+
State::Waiting(_) => panic!("shouldn't see waiting state!"),
289299
}
290300

291301
let (val, f) = match me.spawn.borrow_mut().poll_future_notify(&me.waker, 0) {
@@ -315,8 +325,8 @@ fn _future_to_promise(future: Box<dyn Future<Item = JsValue, Error = JsValue>>)
315325
///
316326
/// This function has the same panic behavior as `future_to_promise`.
317327
pub fn spawn_local<F>(future: F)
318-
where
319-
F: Future<Item = (), Error = ()> + 'static,
328+
where
329+
F: Future<Item = (), Error = ()> + 'static,
320330
{
321331
future_to_promise(
322332
future

0 commit comments

Comments
 (0)