Skip to content

Static thread_local! declarations are moved before Drop is called #140816

Open
@orlp

Description

@orlp

Consider this piece of code:

use std::sync::atomic::*;

static UNIQ_ID: AtomicU64 = AtomicU64::new(0);

struct Foo(u64);

impl Foo {
    fn new() -> Foo {
        Foo(UNIQ_ID.fetch_add(1, Ordering::Relaxed))
    }
}

impl Drop for Foo {
    fn drop(&mut self) {
        eprintln!("{} dropped with addr {:p}", self.0, self);
    }
}

fn main() {
    thread_local!(static FOO: Foo = Foo::new());
    FOO.with(|foo| {
        eprintln!("{} living with addr {:p}", foo.0, foo);
    });
}

I would expect the statically declared FOO to not move around after initialization. In fact, Foo might be a type which may not be moved around after initialization at all, such as a type containing pthread_mutex_t. However, we see that it does get moved before the drop:

0 living with addr 0x6000002000a8
0 dropped with addr 0x16f9faaa8

I believe the issue lies here:

// Update the state before running the destructor as it may attempt to
// access the variable.
let val = unsafe { storage.state.get().replace(State::Destroyed(())) };
drop(val);

This code should be changed to not use an enum but rather a separate state flag + cell, so that the value is not moved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-thread-localsArea: Thread local storage (TLS)C-discussionCategory: Discussion or questions that doesn't represent real issues.C-feature-requestCategory: A feature request, i.e: not implemented / a PR.T-libsRelevant to the library team, which will review and decide on the PR/issue.needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions