Skip to content

Commit e98157f

Browse files
committed
Add a poll() method on futures
1 parent d32212c commit e98157f

File tree

3 files changed

+56
-16
lines changed

3 files changed

+56
-16
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
target/
22
**/*.rs.bk
3+
build/

src/player.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ py_class!(pub class Player |py| {
1111
let track = *track.id(py);
1212

1313
let end_of_track = player.load(track, play, position_ms);
14-
PyFuture::new(py, end_of_track, |py, _result| {
15-
Ok(py.None())
14+
PyFuture::new(py, end_of_track, |_py, _result| {
15+
Ok(true)
1616
})
1717
}
1818

src/pyfuture.rs

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,62 @@
1-
use futures::Future;
2-
use std::cell::RefCell;
31
use cpython::{PyResult, PyObject, Python, PythonObject, ToPyObject};
2+
use futures::executor;
3+
use futures::{Future, Async};
4+
use std::cell::RefCell;
5+
use std::sync::Arc;
46

5-
// Workaround rust-lang/rust#28796
67
pub trait Callback : Send {
7-
fn call(self: Box<Self>, py: Python) -> PyResult<PyObject>;
8+
fn poll(&mut self, py: Python) -> PyResult<Option<PyObject>>;
9+
fn wait(&mut self, py: Python) -> PyResult<PyObject>;
10+
}
11+
12+
struct FutureData<F, T> {
13+
future: Option<executor::Spawn<F>>,
14+
then: Option<T>,
815
}
9-
impl <F: Send + for<'a> FnOnce(Python<'a>) -> PyResult<PyObject>> Callback for F {
10-
fn call(self: Box<Self>, py: Python) -> PyResult<PyObject> {
11-
(*self)(py)
16+
17+
struct NoopUnpark;
18+
impl executor::Unpark for NoopUnpark {
19+
fn unpark(&self) {}
20+
}
21+
22+
impl <F, T, U> Callback for FutureData<F, T>
23+
where F: Future + Send + 'static,
24+
T: FnOnce(Python, Result<F::Item, F::Error>) -> PyResult<U> + Send + 'static,
25+
U: ToPyObject
26+
{
27+
fn poll(&mut self, py: Python) -> PyResult<Option<PyObject>> {
28+
let result = {
29+
let future = self.future.as_mut().expect("Future already completed");
30+
match future.poll_future(Arc::new(NoopUnpark)) {
31+
Ok(Async::Ready(v)) => Ok(v),
32+
Err(e) => Err(e),
33+
Ok(Async::NotReady) => return Ok(None),
34+
}
35+
};
36+
37+
self.future = None;
38+
let then = self.then.take().unwrap();
39+
then(py, result).map(|o| Some(o.into_py_object(py).into_object()))
40+
}
41+
42+
fn wait(&mut self, py: Python) -> PyResult<PyObject> {
43+
let mut future = self.future.take().expect("Future already completed");
44+
let result = future.wait_future();
45+
46+
let then = self.then.take().unwrap();
47+
then(py, result).map(|o| o.into_py_object(py).into_object())
1248
}
1349
}
1450

1551
py_class!(pub class PyFuture |py| {
16-
data callback : RefCell<Option<Box<Callback>>>;
52+
data callback : RefCell<Box<Callback>>;
53+
54+
def poll(&self) -> PyResult<Option<PyObject>> {
55+
self.callback(py).borrow_mut().poll(py)
56+
}
1757

1858
def wait(&self) -> PyResult<PyObject> {
19-
let callback = self.callback(py).borrow_mut().take().expect("Future already completed");
20-
callback.call(py)
59+
self.callback(py).borrow_mut().wait(py)
2160
}
2261
});
2362

@@ -27,10 +66,10 @@ impl PyFuture {
2766
T: FnOnce(Python, Result<F::Item, F::Error>) -> PyResult<U> + Send + 'static,
2867
U: ToPyObject
2968
{
30-
PyFuture::create_instance(py, RefCell::new(Some(Box::new(move |py: Python| {
31-
let result = future.wait();
32-
then(py, result).map(|o| o.into_py_object(py).into_object())
33-
}))))
69+
PyFuture::create_instance(py, RefCell::new(Box::new(FutureData {
70+
future: Some(executor::spawn(future)),
71+
then: Some(then),
72+
})))
3473
}
3574
}
3675

0 commit comments

Comments
 (0)