Skip to content
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

runtime (threadpool) does not shutdown when thread panics #209

Closed
steffengy opened this issue Mar 9, 2018 · 13 comments
Closed

runtime (threadpool) does not shutdown when thread panics #209

steffengy opened this issue Mar 9, 2018 · 13 comments

Comments

@steffengy
Copy link
Contributor

extern crate futures;
extern crate tokio;

fn main() {
    // Runtime does not shutdown ever... program never terminates
    tokio::run(futures::lazy(|| -> futures::future::FutureResult<(), ()> {panic!("test")}));
}

If this is expected behavior (requiring the user to handle panics), this should definitely documented.
Else and probably better, this should be handled.

@steffengy steffengy changed the title runtime does not shutdown when thread panics runtime (threadpool) does not shutdown when thread panics Mar 9, 2018
@carllerche
Copy link
Member

Thanks! This is definitely an omission.

@steffengy
Copy link
Contributor Author

steffengy commented Mar 9, 2018

@carllerche
Do you have any solution in mind for that?

In this case one could expect shutdown_on_idle (which tokio::run calls) to propagate the panic.
In other cases, where work is done concurrently, you won't necessarily want a single thread panic to shutdown the entire threadpool/runtime.

futures-cpupool simply uses "CpuFuture" as a "backchannel" (which we do not have for tokio::run) for this,
since you'd expect to handle a failure when polling it anyways.

So some ideas that could work from my point of view so far:

  • Shutdown entire runtime by default, require explicit usage of futures catch_unwind to prevent this in cases where you want it (e.g. webservers, ...)
  • make tokio::run (and other potential methods that should behave this way) use catch_unwind and rethrow the panic after idle shutdown

@carllerche
Copy link
Member

ah, sorry... i'm looking into it right now.

It seems to be exposing some other limitations.

carllerche added a commit to carllerche/tokio that referenced this issue Mar 9, 2018
Currently, the runtime does not shutdown if the runtime handle is
dropped. This can happen during a panic or when the value is simply
dropped.

This patch forces the runtime to shutdown if it is not explicitly
shutdown.

Fixes tokio-rs#209
@carllerche
Copy link
Member

I opened #214.

This shuts down the worker threads, but many not result in all spawned futures to be dropped. These futures aren't leaked per se as they are tied to their notification handles, but this is probably not what is intended.

carllerche added a commit to carllerche/tokio that referenced this issue Mar 9, 2018
Currently, the runtime does not shutdown if the runtime handle is
dropped. This can happen during a panic or when the value is simply
dropped.

This patch forces the runtime to shutdown if it is not explicitly
shutdown.

Fixes tokio-rs#209
@steffengy
Copy link
Contributor Author

steffengy commented Mar 9, 2018

@carllerche
#214 doesn't fix the example I posted above.
This is because the worker panics, which only drops the Worker but not the runtime.
Then the runtime blocks until the workers become idle, which they can't because they are dead.

@carllerche
Copy link
Member

Ah right, i read it wrong...

carllerche added a commit to carllerche/tokio that referenced this issue Mar 10, 2018
If a future panics from within the context of a thread pool, the pool
should not be impacted. To do this, polling the future is wrapped with a
catch_unwind. Extra care is taken to ensure that `thread::panicking()`
is set from within the future's drop handle.

Fixes tokio-rs#209
@carllerche
Copy link
Member

I pushed #216 which wraps the task with a catch_unwind. This will not bubble up to the caller of tokio::run as the future passed to run is just another future submitted to the thread pool.

If the caller wishes to know if the future panicked, that should be handled by the caller.

@steffengy
Copy link
Contributor Author

@carllerche That sounds reasonable and works.

carllerche added a commit that referenced this issue Mar 13, 2018
If a future panics from within the context of a thread pool, the pool
should not be impacted. To do this, polling the future is wrapped with a
catch_unwind. Extra care is taken to ensure that `thread::panicking()`
is set from within the future's drop handle.

Fixes #209
carllerche added a commit that referenced this issue Mar 13, 2018
Currently, the runtime does not shutdown if the runtime handle is
dropped. This can happen during a panic or when the value is simply
dropped.

This patch forces the runtime to shutdown if it is not explicitly
shutdown.

Fixes #209
@Marwes
Copy link
Contributor

Marwes commented May 7, 2018

@carllerche

If the caller wishes to know if the future panicked, that should be handled by the caller.

What would be the best way of handling this? The following is the best/first thing I could come up with :/

pub fn run_no_panic_catch<F>(fut: F)
where
    F: Future<Item = (), Error = ()> + Send + ::std::panic::UnwindSafe + 'static,
{
    use std::sync::{Arc, Mutex};

    let error_result = Arc::new(Mutex::new(None));
    {
        let error_result = error_result.clone();
        tokio::run(
            fut.catch_unwind()
                .map_err(move |err| {
                    *error_result.lock().unwrap() = Some(err);
                })
                .and_then(|result| result),
        );
    }

    if let Some(err) = Arc::try_unwrap(error_result).unwrap().into_inner().unwrap() {
        panic!(err)
    }
}

It also seems that #216 have suppressed some tests in tokio itself like

tokio::run(create_client_server_future());
which will not fail due to panics anymore.

@carllerche
Copy link
Member

@Marwes you can detect if a panic happened by adding a Drop impl for the future. However, if you want to prevent panics for a future, that is another issue. Why do you need to prevent a future to panic?

The threadpool already internally catches panics, so it should not kill a thread.

@Marwes
Copy link
Contributor

Marwes commented May 7, 2018

The code is from a test so if it panics for any reason I want to treat that as a test failure.

@Marwes
Copy link
Contributor

Marwes commented May 7, 2018

I guess panics from futures dispatched onto a separate thread might still be supressed in that case though? 🤔 Is there a reliable way to detect panics even if they are caught on a thread boundary? Or must tests be written in a way to fail despite that?

@carllerche
Copy link
Member

What you probably want is to implement a future by hand with a drop impl that checks is the Future was dropped without yielding a value

ry added a commit to ry/deno that referenced this issue Apr 14, 2019
ry added a commit to ry/deno that referenced this issue Apr 14, 2019
This is to work around Tokio's panic recovery feautre.
Ref tokio-rs/tokio#495
Ref tokio-rs/tokio#209
Ref denoland#1311
Fixes denoland#2097
ry added a commit to ry/deno that referenced this issue Apr 14, 2019
This is to work around Tokio's panic recovery feature.
Ref tokio-rs/tokio#495
Ref tokio-rs/tokio#209
Ref denoland#1311
Fixes denoland#2097
ry added a commit to ry/deno that referenced this issue Apr 14, 2019
This is to work around Tokio's panic recovery feature.
Ref tokio-rs/tokio#495
Ref tokio-rs/tokio#209
Ref denoland#1311
Fixes denoland#2097
ry added a commit to denoland/deno that referenced this issue Apr 14, 2019
This is to work around Tokio's panic recovery feature.
Ref tokio-rs/tokio#495
Ref tokio-rs/tokio#209
Ref #1311
Fixes #2097
Tzikas added a commit to Tzikas/squares that referenced this issue Sep 10, 2019
This is to work around Tokio's panic recovery feature.
Ref tokio-rs/tokio#495
Ref tokio-rs/tokio#209
Ref denoland/deno#1311
Fixes #2097
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

3 participants