Skip to content

internal: no_std thread local - #6356

Open
Person-93 wants to merge 10 commits into
PyO3:mainfrom
Person-93:no_std_thread_local
Open

internal: no_std thread local#6356
Person-93 wants to merge 10 commits into
PyO3:mainfrom
Person-93:no_std_thread_local

Conversation

@Person-93

Copy link
Copy Markdown
Contributor

I've re-implemented LocalKey in terms of python's tss api.

There is currently only one place that uses thread locals, and that is internal/state.rs. It stores the ATTACH_COUNT in a thread local Cell. If the plan is to eventually re-write that file using the PyThreadState functions, it can probably be done without using thread local storage at all.

Comment thread src/platform/thread/thread_local.rs Outdated
@Person-93
Person-93 force-pushed the no_std_thread_local branch from 53c7ed4 to 453afd9 Compare August 29, 2026 02:26

@davidhewitt davidhewitt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, generally seems fine, a couple of thoughts.

I have a feeling that due to the way we currently implement Python::attach, even if we migrate to PEP 788 APIs (which we should definitely do) then we'll still need thread-locals to handle internal state. Maybe if we exposed a more complex (deliberate) thread-state API to users, we could avoid the thread locals, but that isn't a clear enough idea in my head nor am I sure it'd be worth the churn for most users.

Comment thread src/platform/thread/thread_local.rs Outdated
Comment on lines +175 to +184
cfg_select! {
Py_LIMITED_API => {
// SAFETY: inner is returned by PyThread_tss_alloc and is not used after this call
unsafe { PyThread_tss_free(self.inner.load(Ordering::SeqCst)) };
},
_ => {
// SAFETY: inner is not used again after this call
unsafe { PyThread_tss_delete(self.inner.get()) };
},
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess in both of these cases all the values stored for each thread are leaked?

Is there a way that we can trigger destruction of thread-local values on native thread exit? It might not matter for the types which we currently store in thread locals, maybe not ever. So this might just be a question for sake of curiosity which we could document here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It would require calling platform-specific APIs.

@Person-93

Copy link
Copy Markdown
Contributor Author

While I was working on the requested changes, I found another possible race condition.

Thread A checks the state and sees that it's accessible.
Thread B starts running the destructor and free/de-inits the tss.
Thread A tries to use the tss key to get its value.

I have no idea if this is actually possible, but the only fix I can think of is not to have a destructor and leak the tss key.

@Person-93
Person-93 force-pushed the no_std_thread_local branch from 26cdcb4 to 4a5424c Compare August 31, 2026 01:54
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