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

std: Add a variant of thread locals with const init #83416

Merged
merged 1 commit into from
Apr 16, 2021

Conversation

alexcrichton
Copy link
Member

This commit adds a variant of the thread_local! macro as a new
thread_local_const_init! macro which requires that the initialization
expression is constant (e.g. could be stuck into a const if so
desired). This form of thread local allows for a more efficient
implementation of LocalKey::with both if the value has a destructor
and if it doesn't. If the value doesn't have a destructor then with
should desugar to exactly as-if you use #[thread_local] given
sufficient inlining.

The purpose of this new form of thread locals is to precisely be
equivalent to #[thread_local] on platforms where possible for values
which fit the bill (those without destructors). This should help close
the gap in performance between thread_local!, which is safe, relative
to #[thread_local], which is not easy to use in a portable fashion.

@rust-highfive
Copy link
Collaborator

r? @sfackler

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 23, 2021
@rust-log-analyzer

This comment has been minimized.

// If the platform has support for `#[thread_local]`, use it.
#[cfg(all(
target_thread_local,
not(all(target_arch = "wasm32", not(target_feature = "atomics"))),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does wasm32 declare support for target_thread_local?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, yes, beacuse it's supported with atomics enabled. Last I checked (a year or two ago) it just didn't behave well in LLVM if atomics was disabled.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably best to file an issue tracking that.

@@ -442,6 +558,12 @@ pub mod fast {
Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) }
}

pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
unsafe {
register_dtor(a, dtor);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this infinitely recursive?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this is actually recursing into a free-function so this shouldn't be infinitely recursive. The only purpose of this is for the macro-generated code to have a public function to call into, but I can document this better too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah missed this was an associated function.

@sfackler
Copy link
Member

Is this intended for internal-only use, or do you see us stabilizing this at some point? It seems pretty unfortunate to have two macros like this in a stable public API.

@alexcrichton
Copy link
Member Author

My intent would be to have this stabilized at some point. This gap in Rust's thread-local support has come up from time to time and I'd hope that this would get us much closer to closing the gap on performance.

I agree though that the API isn't great. Ideally I'd like to do something like test whether the input expression to thread_local! is const and change the expansion of the macro based on that, but I doubt we're likely to get such a feature any time soon. Another alternative would be alternative syntax in the original macro, such as:

thread_local!(const FOO: u32 = 3);
thread_local!(static FOO: u32 = const 3);

(etc)

One part I wasn't sure about though is that if an alternative syntax is chosen I'm not sure how to make just the new syntax unstable while keeping the current syntax stable.

@sfackler
Copy link
Member

sfackler commented Apr 2, 2021

Oops, missed that you replied!

One option is the const { ... } blocks that have been (will be?) added to force const evaluation of an expression:

thread_local! {
    static FOO: u32 = const { 3 };
}

@alexcrichton
Copy link
Member Author

Oh nice! I've updated to use that syntax (and also discovered a way to make only some arms of a macro unstable while the others are stable)

unsafe {
debug_assert_eq!(STATE, 1);
STATE = 2;
drop(ptr.read());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this could use drop_in_place?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this specifically avoids that since it's caused issues in the past with macOS and TLS I think, but I'll add some comments to this effect.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope turns out I was misremembering, updated to use drop_in_place

@sfackler
Copy link
Member

r=me with a tracking issue and that one nit addressed.

@alexcrichton
Copy link
Member Author

I've filed #84223 to track this feature and #84224 for the wasm issue with target_thread_local being set when it probably shouldn't.

@bors: r=sfackler

@bors
Copy link
Contributor

bors commented Apr 15, 2021

📌 Commit 82cef56b15139a03092ef5d6636dbfa3f7e722b4 has been approved by sfackler

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 15, 2021
@rust-log-analyzer

This comment has been minimized.

@alexcrichton
Copy link
Member Author

@bors: r-

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Apr 15, 2021
This commit adds a variant of the `thread_local!` macro as a new
`thread_local_const_init!` macro which requires that the initialization
expression is constant (e.g. could be stuck into a `const` if so
desired). This form of thread local allows for a more efficient
implementation of `LocalKey::with` both if the value has a destructor
and if it doesn't. If the value doesn't have a destructor then `with`
should desugar to exactly as-if you use `#[thread_local]` given
sufficient inlining.

The purpose of this new form of thread locals is to precisely be
equivalent to `#[thread_local]` on platforms where possible for values
which fit the bill (those without destructors). This should help close
the gap in performance between `thread_local!`, which is safe, relative
to `#[thread_local]`, which is not easy to use in a portable fashion.
@alexcrichton
Copy link
Member Author

@bors: r=sfackler

@bors
Copy link
Contributor

bors commented Apr 16, 2021

📌 Commit 82cef56b15139a03092ef5d6636dbfa3f7e722b4 has been approved by sfackler

@bors bors removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Apr 16, 2021
@bors bors added the S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. label Apr 16, 2021
@alexcrichton
Copy link
Member Author

@bors: r=sfackler

@bors
Copy link
Contributor

bors commented Apr 16, 2021

📌 Commit c6eea22 has been approved by sfackler

@bors
Copy link
Contributor

bors commented Apr 16, 2021

⌛ Testing commit c6eea22 with merge 0cc00c4...

@bors
Copy link
Contributor

bors commented Apr 16, 2021

☀️ Test successful - checks-actions
Approved by: sfackler
Pushing 0cc00c4 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Apr 16, 2021
@bors bors merged commit 0cc00c4 into rust-lang:master Apr 16, 2021
@rustbot rustbot added this to the 1.53.0 milestone Apr 16, 2021
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Apr 19, 2021
…=alexcrichton

fix aliasing violations in thread_local_const_init

Fixes rust-lang#83416 (comment)

r? `@alexcrichton` `@sfackler`
.unwrap();

unsafe {
HITS = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes that the TLS destructors have run by the time join() returns. This is currently not the case for the x86_64-fortanix-unknown-sgx target.

I'm a bit torn on whether this is the bug in the x86_64-fortanix-unknown-sgx implementation or this assumption is invalid.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TLS destructors need to run on the thread that is in the process of exiting. I think join() shouldn't return until thread is completely done exiting and thus would have finished running the TLS destructors.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, having a thread do anything after join returned is highly suspicious.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #84409

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Apr 23, 2021
…r=dtolnay

Ensure TLS destructors run before thread joins in SGX

The excellent test is from `@jethrogb`

For context see: rust-lang#83416 (comment)
JohnTitor added a commit to JohnTitor/rust that referenced this pull request Apr 23, 2021
…r=dtolnay

Ensure TLS destructors run before thread joins in SGX

The excellent test is from ``@jethrogb``

For context see: rust-lang#83416 (comment)
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Apr 24, 2021
…r=dtolnay

Ensure TLS destructors run before thread joins in SGX

The excellent test is from ```@jethrogb```

For context see: rust-lang#83416 (comment)
@matklad
Copy link
Member

matklad commented May 3, 2021

Oh wow, this is awesome, thanks alexcrichton!

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 4, 2021
…r=dtolnay

Ensure TLS destructors run before thread joins in SGX

The excellent test is from `@jethrogb`

For context see: rust-lang#83416 (comment)
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 4, 2021
…r=dtolnay

Ensure TLS destructors run before thread joins in SGX

The excellent test is from ``@jethrogb``

For context see: rust-lang#83416 (comment)
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 6, 2021
…r=jethrogb

Ensure TLS destructors run before thread joins in SGX

The excellent test is from `@jethrogb`

For context see: rust-lang#83416 (comment)
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 6, 2021
…r=jethrogb

Ensure TLS destructors run before thread joins in SGX

The excellent test is from ``@jethrogb``

For context see: rust-lang#83416 (comment)
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 6, 2021
…r=jethrogb

Ensure TLS destructors run before thread joins in SGX

The excellent test is from ```@jethrogb```

For context see: rust-lang#83416 (comment)
@alexcrichton alexcrichton deleted the const-thread-local branch September 2, 2021 16:33
// just get going.
if !$crate::mem::needs_drop::<$t>() {
#[thread_local]
static mut VAL: $t = $init;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mut here seems unnecessary.

JohnTitor added a commit to JohnTitor/rust that referenced this pull request Apr 28, 2023
…d, r=thomcc

Document `const {}` syntax for `std::thread_local`.

It exists and is pretty cool. More people should use it.

It was added in rust-lang#83416 and stabilized in rust-lang#91355 with the tracking issue rust-lang#84223.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.