diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000000..fda23657697 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,3 @@ +[[disallowed-types]] +path = "std::thread::ThreadId" +reason = "Use crate::platform::ThreadId for `no_std` compatibility" diff --git a/newsfragments/6056.added.md b/newsfragments/6056.added.md new file mode 100644 index 00000000000..b54c3f32018 --- /dev/null +++ b/newsfragments/6056.added.md @@ -0,0 +1 @@ +Add `PyThread_get_thread_ident` to the ffi crate diff --git a/pyo3-ffi/src/lib.rs b/pyo3-ffi/src/lib.rs index 04048f287eb..8d075cb39fc 100644 --- a/pyo3-ffi/src/lib.rs +++ b/pyo3-ffi/src/lib.rs @@ -486,6 +486,7 @@ pub use self::pyport::*; pub use self::pystate::*; pub use self::pystrtod::*; pub use self::pythonrun::*; +pub use self::pythread::*; pub use self::pytypedefs::*; pub use self::rangeobject::*; pub use self::refcount::*; @@ -578,7 +579,7 @@ mod pythonrun; // skipped pystrhex.h // skipped pystrcmp.h mod pystrtod; -// skipped pythread.h +mod pythread; // skipped pytime.h mod pytypedefs; mod rangeobject; diff --git a/pyo3-ffi/src/pythread.rs b/pyo3-ffi/src/pythread.rs new file mode 100644 index 00000000000..898d55bb5d6 --- /dev/null +++ b/pyo3-ffi/src/pythread.rs @@ -0,0 +1,6 @@ +use core::ffi::c_ulong; + +extern_libpython! { + #[cfg_attr(PyPy, link_name = "PyPyThread_get_thread_ident")] + pub fn PyThread_get_thread_ident() -> c_ulong; +} diff --git a/src/err/err_state.rs b/src/err/err_state.rs index 2efc43ed4f5..4666cc188af 100644 --- a/src/err/err_state.rs +++ b/src/err/err_state.rs @@ -3,11 +3,9 @@ use crate::platform::prelude::*; use core::cell::UnsafeCell; -use std::{ - sync::{Mutex, Once}, - thread::ThreadId, -}; +use std::sync::{Mutex, Once}; +use crate::platform::thread::{self, ThreadId}; #[cfg(not(Py_3_12))] use crate::sync::MutexExt; use crate::{ @@ -98,7 +96,7 @@ impl PyErrState { // re-entrancy guarantees. if let Some(thread) = self.normalizing_thread.lock().unwrap().as_ref() { assert!( - !(*thread == std::thread::current().id()), + !(*thread == thread::current().id()), "Re-entrant normalization of PyErrState detected" ); } @@ -109,7 +107,7 @@ impl PyErrState { self.normalizing_thread .lock() .unwrap() - .replace(std::thread::current().id()); + .replace(thread::current().id()); // Safety: no other thread can access the inner value while we are normalizing it. let state = unsafe { diff --git a/src/impl_/pyclass.rs b/src/impl_/pyclass.rs index 273bf25c3be..6e932015296 100644 --- a/src/impl_/pyclass.rs +++ b/src/impl_/pyclass.rs @@ -3,6 +3,7 @@ #[allow(unused_imports, reason = "conditionally used")] use crate::platform::prelude::*; +use crate::platform::thread; use crate::{ exceptions::{PyAttributeError, PyNotImplementedError, PyRuntimeError}, ffi, @@ -24,7 +25,7 @@ use core::{ marker::PhantomData, ptr::{self, NonNull}, }; -use std::{sync::Mutex, thread}; +use std::sync::Mutex; mod assertions; pub mod doc; diff --git a/src/impl_/pyclass/lazy_type_object.rs b/src/impl_/pyclass/lazy_type_object.rs index 2142b1d4568..cf9bcf23767 100644 --- a/src/impl_/pyclass/lazy_type_object.rs +++ b/src/impl_/pyclass/lazy_type_object.rs @@ -2,8 +2,8 @@ #![allow(clippy::undocumented_unsafe_blocks)] use crate::platform::prelude::*; +use crate::platform::thread::{self, ThreadId}; use core::{ffi::CStr, marker::PhantomData}; -use std::thread::{self, ThreadId}; #[cfg(Py_3_14)] use crate::err::error_on_minusone; diff --git a/src/platform.rs b/src/platform.rs index 924f818dc55..a254a57be32 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -20,3 +20,5 @@ pub use hashbrown::{HashMap, HashSet}; // TODO conditionally import these based on "std" feature #[cfg(not(feature = "hashbrown"))] pub use std::collections::{HashMap, HashSet}; + +pub mod thread; diff --git a/src/platform/thread.rs b/src/platform/thread.rs new file mode 100644 index 00000000000..7cc097d894a --- /dev/null +++ b/src/platform/thread.rs @@ -0,0 +1,26 @@ +use core::ffi::c_ulong; +use core::num::NonZero; + +use pyo3_ffi::PyThread_get_thread_ident; + +#[must_use] +pub fn current() -> Thread { + Thread { + // SAFETY: PyThread_get_thread_ident never returns zero + // https://docs.python.org/3/c-api/threads.html#c.PyThread_get_thread_ident + id: ThreadId(unsafe { NonZero::new_unchecked(PyThread_get_thread_ident()) }), + } +} + +pub struct Thread { + id: ThreadId, +} + +impl Thread { + pub fn id(&self) -> ThreadId { + self.id + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct ThreadId(NonZero);