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

Implement support for multiple TLS destructors on macOS #3739

Merged
merged 1 commit into from
Jul 9, 2024

Conversation

joboet
Copy link
Member

@joboet joboet commented Jul 7, 2024

I want to get rid of this #[cfg] block in std, but it is currently required for miri, as it does not support multiple macOS TLS destructors. This is not true for the platform itself, however, as can be observed in the implementation.

@joboet joboet force-pushed the macos_tls_dtors branch from 34419bc to 098770e Compare July 7, 2024 13:15
@RalfJung
Copy link
Member

RalfJung commented Jul 7, 2024

It is however UB to set another TLS dtor while TLS dtors are running, as far as I know. So Miri should probably detect that.

Curiously this could also mean one can cause UB in the std test suite, once that cfg is removed, by having both a realstd::thread_local and a std::thread_local, and then initializing one in the dtor of the other.

Copy link
Member

@RalfJung RalfJung left a comment

Choose a reason for hiding this comment

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

Please also add a test that directly calls _tlv_atexit, twice, and ensures both get run.

src/shims/tls.rs Outdated
/// Add a thread local storage for the given thread. This function is used to
/// implement `_tlv_atexit` shim on MacOS.
/// FIXME: also implement `__cxa_thread_atexit_impl` for Linux using this, see
/// <https://sourceware.org/glibc/wiki/Destructor%20support%20for%20thread_local%20variables>
Copy link
Member

Choose a reason for hiding this comment

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

This seems better suited for an issue rather than a FIXME in the code.

src/shims/tls.rs Outdated
for (instance, data) in dtors.into_iter().rev() {
trace!("Running macos dtor {:?} on {:?} at {:?}", instance, data, thread_id);

this.call_function(
Copy link
Member

Choose a reason for hiding this comment

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

No this is not right. call_function just pushes the stack frame, so what this does is push a stack frame for each of these functions and then run them all, making the stack look as if the 2nd dtor was called by the first.

There's a big surrounding interpreter loop here, and the next dtor needs to be started only once this dtor finishes running, when the stack is empty again. See what we do for WindowsDtors for how that can be implemented.

Copy link
Member

Choose a reason for hiding this comment

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

To give a bit of context here, each thread has a TlsDtorsState and each time that thread's stack is empty, the main loop calls on_stack_empty on it. If that returns Pending, the thread sticks around. This may also push a new stack frame, in which case that now gets executed, until the stack is empty and on_stack_empty is called again. This repeats until on_stack_empty returns Ready, at which point the thread is fully terminated and never scheduled again.

@joboet
Copy link
Member Author

joboet commented Jul 7, 2024

It is however UB to set another TLS dtor while TLS dtors are running, as far as I know. So Miri should probably detect that.

It isn't in newer versions of macOS, they fixed that bug.

@RalfJung
Copy link
Member

RalfJung commented Jul 7, 2024

And that is an intentional guarantee?

Seems like the behavior is to run the newest things first. So probably what should happen is quite different from WindowsDtors then. We have a new MacosDtors state that we jump to on macOS targets. It calls schedule_macos_tls_dtor, which should take the latest dtor from the list and run it, but we stay in the MacosDtors state. So when that dtor is done, we again take the latest off the list (including when more were added now). This repeats until on_stack_empty in the MacosDtors state sees that the macOS dtor list is empty; then we move on to PthreadDtors(Default::default()).

Please make sure the test covers "registering a dtor inside the dtor", then.

@joboet
Copy link
Member Author

joboet commented Jul 7, 2024

It seems very unlikely that they would remove that fix. But of course, this is Apple we're talking about, so I don't think we'll ever get a proper guarantee. It doesn't matter too much for us anyway, since we still support the older platforms that crash.

@joboet joboet force-pushed the macos_tls_dtors branch 3 times, most recently from c9bc481 to de25371 Compare July 7, 2024 14:11
src/shims/tls.rs Outdated
@@ -202,6 +191,8 @@ impl<'tcx> TlsData<'tcx> {
for TlsEntry { data, .. } in self.keys.values_mut() {
data.remove(&thread_id);
}

self.macos_thread_dtors.remove(&thread_id);
Copy link
Member

Choose a reason for hiding this comment

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

Aren't these always empty because we ran the destructors?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but it seems neater to free the memory of the Vec here.

Copy link
Member

Choose a reason for hiding this comment

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

Can you add an assertion ensuring the vector we pop is empty?

tests/pass/tls/macos_tlv_atexit.rs Outdated Show resolved Hide resolved
src/shims/tls.rs Show resolved Hide resolved
where
F: FnOnce() + 'static,
{
unsafe extern "C" fn run<F>(data: *mut u8)
Copy link
Member

Choose a reason for hiding this comment

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

Please add a comment saying that data is expected to actually be a Box<F>

where
F: FnOnce() + 'static,
{
unsafe { (*Box::from_raw(data as *mut F))() }
Copy link
Member

Choose a reason for hiding this comment

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

It took me a bit to realize you are also calling the function here. Please let-bind the Box::from_raw(data as *mut F) so that this becomes more clear.

tests/pass/tls/macos_tlv_atexit.rs Outdated Show resolved Hide resolved
@RalfJung
Copy link
Member

RalfJung commented Jul 8, 2024

Looks good, thanks. :)
Please squash the commits.

@joboet joboet force-pushed the macos_tls_dtors branch from f364f90 to 22b6295 Compare July 8, 2024 20:28
@RalfJung
Copy link
Member

RalfJung commented Jul 9, 2024

@bors r+

@bors
Copy link
Contributor

bors commented Jul 9, 2024

📌 Commit 22b6295 has been approved by RalfJung

It is now in the queue for this repository.

@bors
Copy link
Contributor

bors commented Jul 9, 2024

⌛ Testing commit 22b6295 with merge b0dd0a8...

@bors
Copy link
Contributor

bors commented Jul 9, 2024

☀️ Test successful - checks-actions
Approved by: RalfJung
Pushing b0dd0a8 to master...

@bors bors merged commit b0dd0a8 into rust-lang:master Jul 9, 2024
8 checks passed
joboet added a commit to joboet/rust that referenced this pull request Jul 24, 2024
With rust-lang/miri#3739 merged, the deduplication hack is no longer necessary.
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jul 24, 2024
std: use duplicate thread local state in tests

With rust-lang/miri#3739 merged, the deduplication hack is no longer necessary.
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jul 24, 2024
std: use duplicate thread local state in tests

With rust-lang/miri#3739 merged, the deduplication hack is no longer necessary.
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Jul 25, 2024
Rollup merge of rust-lang#128135 - joboet:reduplicate_tls, r=tgross35

std: use duplicate thread local state in tests

With rust-lang/miri#3739 merged, the deduplication hack is no longer necessary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants