Skip to content

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

Closed
@pvillela

Description

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>

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

A-threadArea: `std::thread`A-thread-localsArea: Thread local storage (TLS)C-bugCategory: This is a bug.T-libsRelevant to the library team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions