Skip to content
This repository has been archived by the owner on Apr 2, 2018. It is now read-only.

Cannot call poll once value is consumed #36

Open
jedisct1 opened this issue Dec 23, 2017 · 5 comments
Open

Cannot call poll once value is consumed #36

jedisct1 opened this issue Dec 23, 2017 · 5 comments

Comments

@jedisct1
Copy link

Hi Carl,

Nested timers combined with futures-await lead to a panic with the above message:

#![feature(proc_macro, conservative_impl_trait, generators)]
extern crate futures_await as futures;
extern crate tokio_core;
extern crate tokio_timer;
use std::time::Duration;
use tokio_core::reactor::Core;
use futures::prelude::*;

#[async]
fn ola(lv: u32) -> Result<u32, String> {
    let wheel = tokio_timer::wheel().build();
    let timer = wheel.sleep(Duration::new(5, 0));
    let _ = await!(timer);
    println!("ola");
    Ok(lv)
}

#[async]
fn grr() -> Result<u32, ()> {
    let wheel = tokio_timer::wheel().build();
    let ft = ola(42).map_err(|_| ());
    let timer = wheel.timeout(ft, Duration::new(10, 0));
    await!(timer)
}

fn main() {
    let mut core = Core::new().unwrap();
    core.run(grr()).unwrap();
}
thread 'main' panicked at 'cannot call poll once value is consumed'

(fired from await!(timer) in the grr() function).

Just swapping two lines, i.e. replacing:

    let wheel = tokio_timer::wheel().build();
    let ft = ola(42).map_err(|_| ());
    let timer = wheel.timeout(ft, Duration::new(10, 0));

with

    let ft = ola(42).map_err(|_| ());
    let wheel = tokio_timer::wheel().build();
    let timer = wheel.timeout(ft, Duration::new(10, 0));

makes the panic go awayand the executable work as expected.

Any idea why? Alex Crichton suspects a bug in tokio-timer.

@dwrensha
Copy link

dwrensha commented Dec 24, 2017

I suspect a rustc bug in feature(generators). The Timeout future gets polled only once and finds that its self.future is None. That should be impossible.

Reduced to avoid external dependency on futures-await:

#![feature(conservative_impl_trait, generators, generator_trait)]
extern crate futures;
extern crate tokio_core;
extern crate tokio_timer;
use std::time::Duration;
use tokio_core::reactor::Core;
use futures::prelude::*;

pub mod __rt {
    pub use std::ops::Generator;
    use futures::Poll;
    use futures::{Future, Async};
    use std::ops::GeneratorState;

    pub trait MyFuture<T: IsResult>: Future<Item=T::Ok, Error = T::Err> {}

    impl<F, T> MyFuture<T> for F
        where F: Future<Item = T::Ok, Error = T::Err > + ?Sized,
              T: IsResult
    {}

    pub trait IsResult {
        type Ok;
        type Err;

        fn into_result(self) -> Result<Self::Ok, Self::Err>;
    }
    impl<T, E> IsResult for Result<T, E> {
        type Ok = T;
        type Err = E;

        fn into_result(self) -> Result<Self::Ok, Self::Err> { self }
    }

    struct GenFuture<T>(T);

    pub enum Mu {}

    pub fn gen<T>(gen: T) -> impl MyFuture<T::Return>
        where T: Generator<Yield = Async<Mu>>,
              T::Return: IsResult,
    {
        GenFuture(gen)
    }

    impl<T> Future for GenFuture<T>
        where T: Generator<Yield = Async<Mu>>,
              T::Return: IsResult,
    {
        type Item = <T::Return as IsResult>::Ok;
        type Error = <T::Return as IsResult>::Err;

        fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
            match self.0.resume() {
                GeneratorState::Yielded(Async::NotReady)
                    => Ok(Async::NotReady),
                GeneratorState::Yielded(Async::Ready(mu))
                    => match mu {},
                GeneratorState::Complete(e)
                    => e.into_result().map(Async::Ready),
            }
        }
    }
}

fn ola(lv: u32) -> impl __rt::MyFuture<Result<u32, String>> + 'static {
    __rt::gen(move || -> Result<u32, String> {
        let __e: Result<u32, String> =
        {
            {
                let wheel = tokio_timer::wheel().build();
                let timer = wheel.sleep(Duration::new(5, 0));
                let mut future = timer;
                let _ = loop {
                    match ::futures::Future::poll(&mut future)
                    {
                        Ok(::futures::Async::Ready(e)) => break Ok(e),
                        Ok(::futures::Async::NotReady) => (),
                        Err(e) => break Err(e),
                    }
                    yield ::futures::Async::NotReady
                };
                println!("ola");
                Ok(lv)
            }
        };

        return __e;
    })
}


fn grr() -> impl Future<Item=u32, Error=()> {
    let wheel = tokio_timer::wheel().build();
    let ft = ola(42).map_err(|_| ());
    let timeout = wheel.timeout(ft, Duration::new(10, 0));
    timeout
}

fn main() {
    let mut core = Core::new().unwrap();
    core.run(grr()).unwrap();
}

@dwrensha
Copy link

dwrensha commented Dec 24, 2017

Perhaps relevant: I am able to reproduce this problem on MacOS, but not on Linux.

@jedisct1
Copy link
Author

/cc @alexcrichton @Zoxc

@dwrensha
Copy link

dwrensha commented Jan 7, 2018

I've been able to reproduce this problem on Linux now, as well.
I've reduced the repro to avoid all external dependencies, and reported upstream: rust-lang/rust#47253.

@dwrensha
Copy link

This is fixed in the latest nightly rustc, which includes rust-lang/rust#47270. Thanks @Zoxc!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants