Skip to content

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

Closed
@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.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions