Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,19 @@ fn configure_pyo3() -> Result<()> {
Ok(())
}

/// Enables a faux `std` feature by default.
///
/// Set env var `PYO3_WIP_NO_STD` to `1` to disable it.
fn configure_wip_no_std() {
println!("cargo:rustc-check-cfg=cfg(wip_feature_std)");
match cargo_env_var("PYO3_WIP_NO_STD") {
Some(no_std) if no_std.trim() == "1" || no_std.trim().eq_ignore_ascii_case("true") => (),
_ => println!("cargo:rustc-cfg=wip_feature_std"),
}
}

fn main() {
configure_wip_no_std();
pyo3_build_config::print_expected_cfgs();
if let Err(e) = configure_pyo3() {
eprintln!("error: {}", e.report());
Expand Down
7 changes: 7 additions & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[[disallowed-types]]
path = "std::sync::Mutex"
reason = "use crate::platform::sync::non_poison::Mutex for `no_std` compatibility"

[[disallowed-types]]
path = "std::sync::Once"
reason = "use crate::platform::sync::Once for no_std compatibility"
6 changes: 3 additions & 3 deletions pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3075,9 +3075,9 @@ impl<'a> PyClassImplsBuilder<'a> {
quote! {
impl #pyo3_path::impl_::pyclass::PyClassWithFreeList for #cls {
#[inline]
fn get_free_list(py: #pyo3_path::Python<'_>) -> &'static ::std::sync::Mutex<#pyo3_path::impl_::freelist::PyObjectFreeList> {
static FREELIST: #pyo3_path::sync::PyOnceLock<::std::sync::Mutex<#pyo3_path::impl_::freelist::PyObjectFreeList>> = #pyo3_path::sync::PyOnceLock::new();
&FREELIST.get_or_init(py, || ::std::sync::Mutex::new(#pyo3_path::impl_::freelist::PyObjectFreeList::with_capacity(#freelist)))
fn get_free_list(py: #pyo3_path::Python<'_>) -> &'static #pyo3_path::platform::sync::non_poison::Mutex<#pyo3_path::impl_::freelist::PyObjectFreeList> {
static FREELIST: #pyo3_path::sync::PyOnceLock<#pyo3_path::platform::sync::non_poison::Mutex<#pyo3_path::impl_::freelist::PyObjectFreeList>> = #pyo3_path::sync::PyOnceLock::new();
&FREELIST.get_or_init(py, || #pyo3_path::platform::sync::non_poison::Mutex::new(#pyo3_path::impl_::freelist::PyObjectFreeList::with_capacity(#freelist)))
}
}
}
Expand Down
13 changes: 7 additions & 6 deletions src/coroutine/cancel.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::platform::sync::non_poison::Mutex;
use crate::{Py, PyAny};
use alloc::sync::Arc;
use core::future::poll_fn;
use core::panic::AssertUnwindSafe;
use core::task::{Context, Poll, Waker};
use std::sync::Mutex;

#[derive(Debug, Default)]
struct Inner {
Expand All @@ -14,7 +15,7 @@ struct Inner {
///
/// Only the last exception thrown can be retrieved.
#[derive(Debug, Default)]
pub struct CancelHandle(Arc<Mutex<Inner>>);
pub struct CancelHandle(Arc<AssertUnwindSafe<Mutex<Inner>>>);

impl CancelHandle {
/// Create a new `CoroutineCancel`.
Expand All @@ -24,12 +25,12 @@ impl CancelHandle {

/// Returns whether the associated coroutine has been cancelled.
pub fn is_cancelled(&self) -> bool {
self.0.lock().unwrap().exception.is_some()
self.0.lock().exception.is_some()
}

/// Poll to retrieve the exception thrown in the associated coroutine.
pub fn poll_cancelled(&mut self, cx: &mut Context<'_>) -> Poll<Py<PyAny>> {
let mut inner = self.0.lock().unwrap();
let mut inner = self.0.lock();
if let Some(exc) = inner.exception.take() {
return Poll::Ready(exc);
}
Expand All @@ -54,11 +55,11 @@ impl CancelHandle {
}

#[doc(hidden)]
pub struct ThrowCallback(Arc<Mutex<Inner>>);
pub struct ThrowCallback(Arc<AssertUnwindSafe<Mutex<Inner>>>);

impl ThrowCallback {
pub(super) fn throw(&self, exc: Py<PyAny>) {
let mut inner = self.0.lock().unwrap();
let mut inner = self.0.lock();
inner.exception = Some(exc);
if let Some(waker) = inner.waker.take() {
waker.wake();
Expand Down
36 changes: 16 additions & 20 deletions src/err/err_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
#![allow(clippy::undocumented_unsafe_blocks)]

use crate::platform::prelude::*;
use core::cell::UnsafeCell;
use std::{
sync::{Mutex, Once},
thread::ThreadId,
};
#[cfg(not(Py_3_12))]
use crate::platform::sync::non_poison::Mutex;
use crate::platform::sync::Once;
use core::cell::{Cell, UnsafeCell};
use std::thread::ThreadId;

#[cfg(not(Py_3_12))]
use crate::sync::MutexExt;
Expand All @@ -23,7 +23,7 @@ pub(crate) struct PyErrState {
// after normalization.
normalized: Once,
// Guard against re-entrancy when normalizing the exception state.
normalizing_thread: Mutex<Option<ThreadId>>,
normalizing_thread: Cell<Option<ThreadId>>,
inner: UnsafeCell<Option<PyErrStateInner>>,
}

Expand Down Expand Up @@ -68,7 +68,7 @@ impl PyErrState {
fn from_inner(inner: PyErrStateInner) -> Self {
Self {
normalized: Once::new(),
normalizing_thread: Mutex::new(None),
normalizing_thread: Cell::new(None),
inner: UnsafeCell::new(Some(inner)),
}
}
Expand Down Expand Up @@ -96,9 +96,10 @@ impl PyErrState {

// Guard against re-entrant normalization, because `Once` does not provide
// re-entrancy guarantees.
if let Some(thread) = self.normalizing_thread.lock().unwrap().as_ref() {
assert!(
!(*thread == std::thread::current().id()),
if let Some(thread) = self.normalizing_thread.get() {
assert_ne!(
thread,
std::thread::current().id(),
"Re-entrant normalization of PyErrState detected"
);
}
Expand All @@ -107,9 +108,7 @@ impl PyErrState {
py.detach(|| {
self.normalized.call_once(|| {
self.normalizing_thread
.lock()
.unwrap()
.replace(std::thread::current().id());
.set(Some(std::thread::current().id()));

// Safety: no other thread can access the inner value while we are normalizing it.
let state = unsafe {
Expand Down Expand Up @@ -143,7 +142,7 @@ pub(crate) struct PyErrStateNormalized {
ptype: Py<PyType>,
pub pvalue: Py<PyBaseException>,
#[cfg(not(Py_3_12))]
ptraceback: std::sync::Mutex<Option<Py<PyTraceback>>>,
ptraceback: Mutex<Option<Py<PyTraceback>>>,
}

impl PyErrStateNormalized {
Expand Down Expand Up @@ -177,7 +176,6 @@ impl PyErrStateNormalized {
pub(crate) fn ptraceback<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
self.ptraceback
.lock_py_attached(py)
.unwrap()
.as_ref()
.map(|traceback| traceback.bind(py).clone())
}
Expand All @@ -193,7 +191,7 @@ impl PyErrStateNormalized {

#[cfg(not(Py_3_12))]
pub(crate) fn set_ptraceback<'py>(&self, py: Python<'py>, tb: Option<Bound<'py, PyTraceback>>) {
*self.ptraceback.lock_py_attached(py).unwrap() = tb.map(Bound::unbind);
*self.ptraceback.lock_py_attached(py) = tb.map(Bound::unbind);
}

#[cfg(Py_3_12)]
Expand Down Expand Up @@ -251,7 +249,7 @@ impl PyErrStateNormalized {
ptype.map(|ptype| PyErrStateNormalized {
ptype: ptype.unbind(),
pvalue: pvalue.expect("normalized exception value missing").unbind(),
ptraceback: std::sync::Mutex::new(ptraceback.map(Bound::unbind)),
ptraceback: Mutex::new(ptraceback.map(Bound::unbind)),
})
}
}
Expand Down Expand Up @@ -291,10 +289,9 @@ impl PyErrStateNormalized {
ptype: self.ptype.clone_ref(py),
pvalue: self.pvalue.clone_ref(py),
#[cfg(not(Py_3_12))]
ptraceback: std::sync::Mutex::new(
ptraceback: Mutex::new(
self.ptraceback
.lock_py_attached(py)
.unwrap()
.as_ref()
.map(|ptraceback| ptraceback.clone_ref(py)),
),
Expand Down Expand Up @@ -350,7 +347,6 @@ impl PyErrStateInner {
pvalue.into_ptr(),
ptraceback
.into_inner()
.unwrap()
.map_or(core::ptr::null_mut(), Py::into_ptr),
),
};
Expand Down
7 changes: 4 additions & 3 deletions src/impl_/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#[allow(unused_imports, reason = "conditionally used")]
use crate::platform::prelude::*;
use crate::platform::sync::non_poison::Mutex;
use crate::{
exceptions::{PyAttributeError, PyNotImplementedError, PyRuntimeError},
ffi,
Expand All @@ -24,7 +25,7 @@ use core::{
marker::PhantomData,
ptr::{self, NonNull},
};
use std::{sync::Mutex, thread};
use std::thread;

mod assertions;
pub mod doc;
Expand Down Expand Up @@ -951,7 +952,7 @@ pub unsafe extern "C" fn alloc_with_freelist<T: PyClassWithFreeList>(
// If this type is a variable type or the subtype is not equal to this type, we cannot use the
// freelist
if nitems == 0 && ptr::eq(subtype, self_type) {
let mut free_list = T::get_free_list(py).lock().unwrap();
let mut free_list = T::get_free_list(py).lock();
if let Some(obj) = free_list.pop() {
drop(free_list);
unsafe { ffi::PyObject_Init(obj.as_ptr(), subtype) };
Expand All @@ -976,7 +977,7 @@ pub unsafe extern "C" fn free_with_freelist<T: PyClassWithFreeList>(obj: *mut c_
T::type_object_raw(Python::assume_attached()),
ffi::Py_TYPE(obj.as_ptr())
);
let mut free_list = T::get_free_list(Python::assume_attached()).lock().unwrap();
let mut free_list = T::get_free_list(Python::assume_attached()).lock();
if let Some(obj) = free_list.insert(obj) {
drop(free_list);
let ty = ffi::Py_TYPE(obj.as_ptr());
Expand Down
9 changes: 4 additions & 5 deletions src/impl_/pyclass/lazy_type_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::thread::{self, ThreadId};

#[cfg(Py_3_14)]
use crate::err::error_on_minusone;
use crate::platform::sync::non_poison::Mutex;
#[allow(deprecated)]
use crate::sync::GILOnceCell;
#[cfg(Py_3_14)]
Expand All @@ -20,8 +21,6 @@ use crate::{
Bound, Py, PyAny, PyClass, PyErr, PyResult, Python,
};

use std::sync::Mutex;

use super::PyClassItemsIter;

/// Lazy type object for PyClass.
Expand Down Expand Up @@ -136,7 +135,7 @@ impl LazyTypeObjectInner {

let thread_id = thread::current().id();
{
let mut threads = self.initializing_threads.lock().unwrap();
let mut threads = self.initializing_threads.lock();
if threads.contains(&thread_id) {
// Reentrant call: just return the type object, even if the
// `tp_dict` is not filled yet.
Expand All @@ -151,7 +150,7 @@ impl LazyTypeObjectInner {
}
impl Drop for InitializationGuard<'_> {
fn drop(&mut self) {
let mut threads = self.initializing_threads.lock().unwrap();
let mut threads = self.initializing_threads.lock();
threads.retain(|id| *id != self.thread_id);
}
}
Expand Down Expand Up @@ -218,7 +217,7 @@ impl LazyTypeObjectInner {
// (No further calls to get_or_init() will try to init, on any thread.)
let mut threads = {
drop(guard);
self.initializing_threads.lock().unwrap()
self.initializing_threads.lock()
};
threads.clear();
Ok(type_object.clone().unbind())
Expand Down
10 changes: 5 additions & 5 deletions src/internal/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ use crate::impl_::panic::PanicTrap;
use crate::platform::prelude::*;
use crate::{ffi, Python};

#[cfg(not(pyo3_disable_reference_pool))]
use crate::platform::sync::non_poison::Mutex;
use core::cell::Cell;
#[cfg_attr(pyo3_disable_reference_pool, allow(unused_imports))]
use core::{mem, ptr::NonNull};
#[cfg(not(pyo3_disable_reference_pool))]
use std::sync::{Mutex, OnceLock};
use std::sync::OnceLock;

std::thread_local! {
/// This is an internal counter in pyo3 monitoring whether this thread is attached to the interpreter.
Expand Down Expand Up @@ -202,11 +204,11 @@ impl ReferencePool {
}

fn register_decref(&self, obj: NonNull<ffi::PyObject>) {
self.pending_decrefs.lock().unwrap().push(obj);
self.pending_decrefs.lock().push(obj);
}

fn drop_deferred_references(&self, _py: Python<'_>) {
let mut pending_decrefs = self.pending_decrefs.lock().unwrap();
let mut pending_decrefs = self.pending_decrefs.lock();
if pending_decrefs.is_empty() {
return;
}
Expand Down Expand Up @@ -382,7 +384,6 @@ mod tests {
!get_pool()
.pending_decrefs
.lock()
.unwrap()
.contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) })
}

Expand All @@ -393,7 +394,6 @@ mod tests {
get_pool()
.pending_decrefs
.lock()
.unwrap()
.contains(&unsafe { NonNull::new_unchecked(obj.as_ptr()) })
}

Expand Down
8 changes: 5 additions & 3 deletions src/interpreter_lifecycle.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// TODO https://github.com/PyO3/pyo3/issues/5487
#![allow(clippy::undocumented_unsafe_blocks)]

use crate::platform::sync::Once;

#[cfg(not(any(PyPy, GraalPy)))]
use crate::{ffi, internal::state::AttachGuard, Python};

static START: std::sync::Once = std::sync::Once::new();
static START: Once = Once::new();

#[cfg(not(any(PyPy, GraalPy)))]
pub(crate) fn initialize() {
// Protect against race conditions when Python is not yet initialized and multiple threads
// concurrently call 'initialize()'. Note that we do not protect against
// concurrent initialization of the Python runtime by other users of the Python C API.
START.call_once_force(|_| unsafe {
START.call_once_force(|| unsafe {
// Use call_once_force because if initialization panics, it's okay to try again.
if ffi::Py_IsInitialized() == 0 {
ffi::Py_InitializeEx(0);
Expand Down Expand Up @@ -127,7 +129,7 @@ pub(crate) fn ensure_initialized() {
initialize();
}

START.call_once_force(|_| unsafe {
START.call_once_force(|| unsafe {
// Use call_once_force because if there is a panic because the interpreter is
// not initialized, it's fine for the user to initialize the interpreter and
// retry.
Expand Down
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,6 @@ pub(crate) mod ffi_ptr_ext;
pub(crate) mod py_result_ext;
pub(crate) mod sealed;

mod platform;

/// Old module which contained some implementation details of the `#[pyproto]` module.
///
/// Prefer using the same content from `pyo3::pyclass`, e.g. `use pyo3::pyclass::CompareOp` instead
Expand Down Expand Up @@ -415,6 +413,9 @@ mod internal;
#[macro_use]
mod internal_tricks;

#[doc(hidden)]
pub mod platform;

// Macro dependencies, also contains macros exported for use across the codebase and
// in expanded macros.
#[doc(hidden)]
Expand Down
2 changes: 2 additions & 0 deletions src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub(crate) mod prelude {
pub use std::eprintln;
}

pub mod sync;

#[cfg(feature = "hashbrown")]
pub use hashbrown::{HashMap, HashSet};

Expand Down
Loading