Skip to content

Future panics if it was polled immediately after it was submitted to the CpuPool #289

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

Closed
dovahcrow opened this issue Dec 13, 2016 · 5 comments

Comments

@dovahcrow
Copy link

This snippet of code reproduces the panic.

extern crate futures;
extern crate futures_cpupool;
use futures::Future;
use futures_cpupool::CpuPool;

fn main() {
    let f = futures::lazy(|| Ok::<u32, u32>(1));
    let pool = CpuPool::new(1);
    let mut c = pool.spawn(f);
    let _ = c.poll();
}
@dwrensha
Copy link
Contributor

dwrensha commented Dec 13, 2016

Yep, when I run that code I get the error message:

thread 'foo' panicked at 'no Task is currently running'

The problem is that Future::poll() is only allowed to be called from a task, because it needs to be able to arrange to be called later if it's not currently ready. (#250 and #136 have some discussion about how this kind of thing can be confusing.) Typically poll() is used in implementations of the Future trait, but not in higher level code.

If you don't need to do any async I/O, then Future::wait() might be sufficient for your needs. It handles creating a task on which to run a future:

    let f1 = futures::lazy(|| Ok::<u32, u32>(1));
    let f2 = futures::lazy(|| Ok::<u32, u32>(2));
    let pool = CpuPool::new(1);
    let mut c1 = pool.spawn(f1);
    let mut c2 = pool.spawn(f2);
    println!("{:?}", c1.join(c2).wait());

If you are going to need to do async I/O, then you'll need some way to manage tasks that are waiting on external events. The easiest path forward in that case is to add a dependency on the tokio-core crate and launch a tokio_core::reactor::Core.

@alexcrichton
Copy link
Member

I think @dwrensha hit the nail on the head, so closing in favor of the linked issues.

@dovahcrow
Copy link
Author

dovahcrow commented Dec 14, 2016

@dwrensha then how do I know a task is finished and get the result outside the worker thread of CpuPool if I'm using the async way? I currently using code like this to check if a future is resolved and send the result to elsewhere in my main thread:

// push the future 
fn push_futures(&mut self, fut: BoxFuture<HQMessage, Error>) {
        let fut = self.future_pool.spawn(fut);
        self.pending_futures.push(fut);
}

// see if they are finished
fn resolve_futures(&mut self, cli: &mut RtmClient) {
        let unresolved: Vec<_> = self.pending_futures
            .drain(..)
            .filter_map(|mut fut| {
                if let Ok(Async::Ready(HQMessage(channel, user, text))) = fut.poll() {
                    let _ = cli.send_message(&channel, &format!("<@{}> {}", user, text));
                    None
                } else {
                    Some(fut)
                }
            })
            .collect();
        self.pending_futures = unresolved;
    }

@Diggsey
Copy link
Contributor

Diggsey commented Jan 1, 2017

@alexcrichton I think there's a legitimate use-case here which is not currently supported (or at least not easy): the ability to check whether a future is complete without setting up any unpark events. (Say your main thread is running a real-time application rather than an event-based one).

At the moment the way to do this is to spawn the future, and then poll the spawned future, passing in a dummy Unpark value.

@dovahcrow
Copy link
Author

dovahcrow commented Jan 2, 2017 via email

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

No branches or pull requests

4 participants