Skip to content

Commit

Permalink
Add a poll() method on futures
Browse files Browse the repository at this point in the history
  • Loading branch information
plietar committed May 14, 2017
1 parent d32212c commit e98157f
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 16 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target/
**/*.rs.bk
build/
4 changes: 2 additions & 2 deletions src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ py_class!(pub class Player |py| {
let track = *track.id(py);

let end_of_track = player.load(track, play, position_ms);
PyFuture::new(py, end_of_track, |py, _result| {
Ok(py.None())
PyFuture::new(py, end_of_track, |_py, _result| {
Ok(true)
})
}

Expand Down
67 changes: 53 additions & 14 deletions src/pyfuture.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,62 @@
use futures::Future;
use std::cell::RefCell;
use cpython::{PyResult, PyObject, Python, PythonObject, ToPyObject};
use futures::executor;
use futures::{Future, Async};
use std::cell::RefCell;
use std::sync::Arc;

// Workaround rust-lang/rust#28796
pub trait Callback : Send {
fn call(self: Box<Self>, py: Python) -> PyResult<PyObject>;
fn poll(&mut self, py: Python) -> PyResult<Option<PyObject>>;
fn wait(&mut self, py: Python) -> PyResult<PyObject>;
}

struct FutureData<F, T> {
future: Option<executor::Spawn<F>>,
then: Option<T>,
}
impl <F: Send + for<'a> FnOnce(Python<'a>) -> PyResult<PyObject>> Callback for F {
fn call(self: Box<Self>, py: Python) -> PyResult<PyObject> {
(*self)(py)

struct NoopUnpark;
impl executor::Unpark for NoopUnpark {
fn unpark(&self) {}
}

impl <F, T, U> Callback for FutureData<F, T>
where F: Future + Send + 'static,
T: FnOnce(Python, Result<F::Item, F::Error>) -> PyResult<U> + Send + 'static,
U: ToPyObject
{
fn poll(&mut self, py: Python) -> PyResult<Option<PyObject>> {
let result = {
let future = self.future.as_mut().expect("Future already completed");
match future.poll_future(Arc::new(NoopUnpark)) {
Ok(Async::Ready(v)) => Ok(v),
Err(e) => Err(e),
Ok(Async::NotReady) => return Ok(None),
}
};

self.future = None;
let then = self.then.take().unwrap();
then(py, result).map(|o| Some(o.into_py_object(py).into_object()))
}

fn wait(&mut self, py: Python) -> PyResult<PyObject> {
let mut future = self.future.take().expect("Future already completed");
let result = future.wait_future();

let then = self.then.take().unwrap();
then(py, result).map(|o| o.into_py_object(py).into_object())
}
}

py_class!(pub class PyFuture |py| {
data callback : RefCell<Option<Box<Callback>>>;
data callback : RefCell<Box<Callback>>;

def poll(&self) -> PyResult<Option<PyObject>> {
self.callback(py).borrow_mut().poll(py)
}

def wait(&self) -> PyResult<PyObject> {
let callback = self.callback(py).borrow_mut().take().expect("Future already completed");
callback.call(py)
self.callback(py).borrow_mut().wait(py)
}
});

Expand All @@ -27,10 +66,10 @@ impl PyFuture {
T: FnOnce(Python, Result<F::Item, F::Error>) -> PyResult<U> + Send + 'static,
U: ToPyObject
{
PyFuture::create_instance(py, RefCell::new(Some(Box::new(move |py: Python| {
let result = future.wait();
then(py, result).map(|o| o.into_py_object(py).into_object())
}))))
PyFuture::create_instance(py, RefCell::new(Box::new(FutureData {
future: Some(executor::spawn(future)),
then: Some(then),
})))
}
}

0 comments on commit e98157f

Please sign in to comment.