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

Scoped thread implicit join completes before thread-locals are dropped #116179

Closed
pvillela opened this issue Sep 26, 2023 · 1 comment
Closed
Assignees
Labels
A-thread Area: `std::thread` A-thread-locals Area: Thread local storage (TLS) C-bug Category: This is a bug. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@pvillela
Copy link

When a scoped thread is implicitly joined, the destructors of thread-local variables are not guaranteed to have completed when the scope is exited. When a scoped thread is explicitly joined, however, the destructors of thread-local variables do complete before the scope is exited.

I tried this code:

use std::{hint::black_box, sync::Mutex, thread, time::Duration};

static MTX: Mutex<()> = Mutex::new(());

fn main() {
    // Implicit join of scoped thread.
    {
        let lock = MTX.lock().unwrap();

        thread::scope(|s| {
            let _h = s.spawn(|| {
                println!("on {:?}", thread::current().id());
                FOO.with(|v| {
                    black_box(v);
                });
            });
        });

        println!("Executed 1st thread scope.");
        drop(lock);
        thread::sleep(Duration::from_millis(100));
    }

    // Explicit join of scoped thread.
    {
        let lock = MTX.lock().unwrap();

        thread::scope(|s| {
            let h = s.spawn(|| {
                println!("on {:?}", thread::current().id());
                FOO.with(|v| {
                    black_box(v);
                });
            });
            h.join().unwrap();
        });

        println!("Executed 2nd thread scope.");
        drop(lock);
        thread::sleep(Duration::from_millis(100));
    }
}

struct Foo(());

impl Drop for Foo {
    fn drop(&mut self) {
        println!("entering Foo::drop on {:?}", thread::current().id());
        let _lock = MTX.lock().unwrap();
        println!("Foo::drop completed on {:?}", thread::current().id());
    }
}

thread_local! {
    static FOO: Foo = Foo(());
}

I expected to see this happen: explanation

  • The code would hang (deadlock) at the end of the first scoped thread block.

Instead, this happened: explanation

  • The code does not hang (deadlock) at the end of the first scoped thread block.
  • As expected, the code does hang (deadlock) at the end of the second scoped thread block, where the thread is joined explicitly..

Meta

The same behaviour is observed on the nightly version nightly-x86_64-unknown-linux-gnu unchanged - rustc 1.74.0-nightly (0288f2e19 2023-09-25).

rustc --version --verbose:

rustc 1.72.1 (d5c2e9c34 2023-09-13)
binary: rustc
commit-hash: d5c2e9c342b358556da91d61ed4133f6f50fc0c3
commit-date: 2023-09-13
host: x86_64-unknown-linux-gnu
release: 1.72.1
LLVM version: 16.0.5
Backtrace

<backtrace>

@pvillela pvillela added the C-bug Category: This is a bug. label Sep 26, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 26, 2023
@WaffleLapkin WaffleLapkin added the T-libs Relevant to the library team, which will review and decide on the PR/issue. label Sep 28, 2023
@workingjubilee workingjubilee added A-atomic Area: Atomics, barriers, and sync primitives A-thread Area: `std::thread` and removed A-atomic Area: Atomics, barriers, and sync primitives labels Sep 29, 2023
@fmease fmease added A-thread-locals Area: Thread local storage (TLS) and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Sep 29, 2023
@workingjubilee workingjubilee added the I-libs-nominated Nominated for discussion during a libs team meeting. label Oct 5, 2023
@m-ou-se m-ou-se self-assigned this Oct 5, 2023
@m-ou-se
Copy link
Member

m-ou-se commented Oct 18, 2023

Merging this issue with #116237, since these are about the same underlying issue of TLS destructors running after thread::scope's implicit join.

@m-ou-se m-ou-se closed this as completed Oct 18, 2023
@dtolnay dtolnay removed the I-libs-nominated Nominated for discussion during a libs team meeting. label Sep 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-thread Area: `std::thread` A-thread-locals Area: Thread local storage (TLS) C-bug Category: This is a bug. 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

7 participants