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

thread::scope clobbers the panic payload #139017

Open
SludgePhD opened this issue Mar 27, 2025 · 4 comments
Open

thread::scope clobbers the panic payload #139017

SludgePhD opened this issue Mar 27, 2025 · 4 comments
Labels
A-panic Area: Panicking machinery A-thread Area: `std::thread` C-bug Category: This is a bug. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@SludgePhD
Copy link

I tried this code:

use std::{panic::catch_unwind, thread};

fn main() {
    let res = catch_unwind(|| {
        thread::scope(|scope| {
            scope.spawn(|| panic!("my panic message with useful information"));
        });
    });
    let payload = res.unwrap_err();
    let message = payload.downcast_ref::<&str>().unwrap();
    dbg!(message);
}

I expected to see this happen: the panic payload from the scoped thread should be forwarded to the main thread using panic::resume_unwind. The payload passed to panic! inside the scope should be retrievable there, and there should not be any additional panics.

Instead, this happened: a second panic with no link to the original is caused by the thread::scope implementation, and the payload is a meaningless "a scoped thread panicked" message.

Meta

rustc --version --verbose:

rustc 1.85.1

beta and nightly also behave like this

@SludgePhD SludgePhD added the C-bug Category: This is a bug. label Mar 27, 2025
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Mar 27, 2025
@lolbinarycat lolbinarycat added T-libs Relevant to the library team, which will review and decide on the PR/issue. A-thread Area: `std::thread` A-panic Area: Panicking machinery labels Mar 27, 2025
@theemathas
Copy link
Contributor

The scope() documentation states:

If you want to handle panics from spawned threads, join them before the end of the scope.

You can get the payload with the following code:

use std::thread;

fn main() {
    let res = thread::scope(|scope| -> thread::Result<()> {
        let join_handle = scope.spawn(|| panic!("my panic message with useful information"));
        join_handle.join()?;
        // Maybe join other threads here
        Ok(())
    });
    let payload = res.unwrap_err();
    let message = payload.downcast_ref::<&str>().unwrap();
    dbg!(message);
}

@SludgePhD
Copy link
Author

Sure, but there is no good reason for the behavior in the first place. Structured concurrency primitives like thread::scope have the opportunity to perfectly forward panics across thread boundaries, it would be a waste not to use it.

@hanna-kruppe
Copy link
Contributor

hanna-kruppe commented Mar 28, 2025

What do you expect to happen the implicit join at the end of the scope finds more than one panicked thread?

@SludgePhD
Copy link
Author

Then any of the panics can be forwarded (which is the same behavior as rayon's). The code running in the threads also has to ensure that it doesn't cause any knock-on panics for this strategy to yield a "useful" panic payload, of course.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-panic Area: Panicking machinery A-thread Area: `std::thread` C-bug Category: This is a bug. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants