-
Notifications
You must be signed in to change notification settings - Fork 758
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
Initial free threaded bindings #4421
Changes from 26 commits
2685691
e8613d5
eeac20c
8eebe43
9e9d77e
79dd1a7
5aaa03a
f4219b3
4ff9a62
57e7e3d
a042e56
7c493eb
da3e50c
77b4d96
96dd13e
dbcb9d2
d9dac3c
9b70b07
3e03dd1
f695203
848c793
6e66cb0
126c97b
162a65e
6103d26
8a9ef2d
e1ddd01
1a088ea
38bc492
a85fa4c
60c26c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* Added bindings for PyMutex. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* Updated FFI bindings for free-threaded CPython 3.13 ABI |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
use std::fmt; | ||
use std::sync::atomic::AtomicU8; | ||
|
||
#[repr(C)] | ||
pub struct PyMutex { | ||
ngoldbaum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pub(crate) _bits: AtomicU8, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at Rust's built-in mutex types, I think we'd want a constructor like: impl PyMutex {
pub const fn new() -> PyMutex {
PyMutex { _bits: AtomicU8::new(0) }
}
} |
||
|
||
impl fmt::Debug for PyMutex { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.debug_struct("PyMutex").finish() | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be a |
||
|
||
extern "C" { | ||
pub fn PyMutex_Lock(m: *mut PyMutex); | ||
pub fn PyMutex_UnLock(m: *mut PyMutex); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,11 @@ | ||
use crate::pyport::{Py_hash_t, Py_ssize_t}; | ||
#[cfg(Py_GIL_DISABLED)] | ||
use crate::PyMutex; | ||
use std::mem; | ||
use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void}; | ||
use std::ptr; | ||
#[cfg(Py_GIL_DISABLED)] | ||
use std::sync::atomic::{AtomicIsize, AtomicU32, AtomicU8, Ordering::Relaxed}; | ||
|
||
#[cfg(Py_LIMITED_API)] | ||
opaque_struct!(PyTypeObject); | ||
|
@@ -22,12 +26,32 @@ pub const _Py_IMMORTAL_REFCNT: Py_ssize_t = { | |
} | ||
}; | ||
|
||
#[cfg(Py_GIL_DISABLED)] | ||
pub const _Py_IMMORTAL_REFCNT_LOCAL: u32 = u32::MAX; | ||
#[cfg(Py_GIL_DISABLED)] | ||
pub const _Py_REF_SHARED_SHIFT: isize = 2; | ||
|
||
#[allow(clippy::declare_interior_mutable_const)] | ||
pub const PyObject_HEAD_INIT: PyObject = PyObject { | ||
#[cfg(py_sys_config = "Py_TRACE_REFS")] | ||
_ob_next: std::ptr::null_mut(), | ||
#[cfg(py_sys_config = "Py_TRACE_REFS")] | ||
_ob_prev: std::ptr::null_mut(), | ||
#[cfg(Py_3_12)] | ||
#[cfg(Py_GIL_DISABLED)] | ||
ob_tid: 0, | ||
#[cfg(Py_GIL_DISABLED)] | ||
_padding: 0, | ||
#[cfg(Py_GIL_DISABLED)] | ||
ob_mutex: PyMutex { | ||
_bits: AtomicU8::new(0), | ||
}, | ||
#[cfg(Py_GIL_DISABLED)] | ||
ob_gc_bits: 0, | ||
#[cfg(Py_GIL_DISABLED)] | ||
ob_ref_local: AtomicU32::new(_Py_IMMORTAL_REFCNT_LOCAL), | ||
#[cfg(Py_GIL_DISABLED)] | ||
ob_ref_shared: AtomicIsize::new(0), | ||
#[cfg(all(not(Py_GIL_DISABLED), Py_3_12))] | ||
ob_refcnt: PyObjectObRefcnt { ob_refcnt: 1 }, | ||
#[cfg(not(Py_3_12))] | ||
ob_refcnt: 1, | ||
|
@@ -67,6 +91,19 @@ pub struct PyObject { | |
pub _ob_next: *mut PyObject, | ||
#[cfg(py_sys_config = "Py_TRACE_REFS")] | ||
pub _ob_prev: *mut PyObject, | ||
#[cfg(Py_GIL_DISABLED)] | ||
pub ob_tid: usize, | ||
ngoldbaum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#[cfg(Py_GIL_DISABLED)] | ||
pub _padding: u16, | ||
#[cfg(Py_GIL_DISABLED)] | ||
pub ob_mutex: PyMutex, // per-object lock | ||
#[cfg(Py_GIL_DISABLED)] | ||
pub ob_gc_bits: u8, // gc-related state | ||
#[cfg(Py_GIL_DISABLED)] | ||
pub ob_ref_local: AtomicU32, // local reference count | ||
#[cfg(Py_GIL_DISABLED)] | ||
pub ob_ref_shared: AtomicIsize, // shared reference count | ||
#[cfg(not(Py_GIL_DISABLED))] | ||
pub ob_refcnt: PyObjectObRefcnt, | ||
#[cfg(PyPy)] | ||
pub ob_pypy_link: Py_ssize_t, | ||
|
@@ -91,6 +128,18 @@ pub unsafe fn Py_Is(x: *mut PyObject, y: *mut PyObject) -> c_int { | |
} | ||
|
||
#[inline] | ||
#[cfg(Py_GIL_DISABLED)] | ||
pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { | ||
let local = (*ob).ob_ref_local.load(Relaxed); | ||
if local == _Py_IMMORTAL_REFCNT_LOCAL { | ||
return _Py_IMMORTAL_REFCNT; | ||
} | ||
let shared = (*ob).ob_ref_shared.load(Relaxed); | ||
Comment on lines
+136
to
+140
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I checked the CPython source to verify these are indeed relaxed loads 👍 |
||
local as Py_ssize_t + Py_ssize_t::from(shared >> _Py_REF_SHARED_SHIFT) | ||
davidhewitt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
#[inline] | ||
#[cfg(not(Py_GIL_DISABLED))] | ||
#[cfg(Py_3_12)] | ||
pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { | ||
(*ob).ob_refcnt.ob_refcnt | ||
|
@@ -134,7 +183,7 @@ pub unsafe fn Py_IS_TYPE(ob: *mut PyObject, tp: *mut PyTypeObject) -> c_int { | |
} | ||
|
||
#[inline(always)] | ||
#[cfg(all(Py_3_12, target_pointer_width = "64"))] | ||
#[cfg(all(not(Py_GIL_DISABLED), Py_3_12, target_pointer_width = "64"))] | ||
pub unsafe fn _Py_IsImmortal(op: *mut PyObject) -> c_int { | ||
(((*op).ob_refcnt.ob_refcnt as crate::PY_INT32_T) < 0) as c_int | ||
} | ||
|
@@ -507,8 +556,14 @@ extern "C" { | |
|
||
#[inline(always)] | ||
pub unsafe fn Py_INCREF(op: *mut PyObject) { | ||
ngoldbaum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// On limited API or with refcount debugging, let the interpreter do refcounting | ||
#[cfg(any(Py_LIMITED_API, py_sys_config = "Py_REF_DEBUG", GraalPy))] | ||
// On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting | ||
// TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. | ||
#[cfg(any( | ||
Py_GIL_DISABLED, | ||
Py_LIMITED_API, | ||
py_sys_config = "Py_REF_DEBUG", | ||
GraalPy | ||
))] | ||
{ | ||
// _Py_IncRef was added to the ABI in 3.10; skips null checks | ||
#[cfg(all(Py_3_10, not(PyPy)))] | ||
|
@@ -523,7 +578,12 @@ pub unsafe fn Py_INCREF(op: *mut PyObject) { | |
} | ||
|
||
// version-specific builds are allowed to directly manipulate the reference count | ||
#[cfg(not(any(any(Py_LIMITED_API, py_sys_config = "Py_REF_DEBUG", GraalPy))))] | ||
#[cfg(not(any( | ||
Py_GIL_DISABLED, | ||
Py_LIMITED_API, | ||
py_sys_config = "Py_REF_DEBUG", | ||
GraalPy | ||
)))] | ||
{ | ||
#[cfg(all(Py_3_12, target_pointer_width = "64"))] | ||
{ | ||
|
@@ -559,9 +619,11 @@ pub unsafe fn Py_INCREF(op: *mut PyObject) { | |
track_caller | ||
)] | ||
pub unsafe fn Py_DECREF(op: *mut PyObject) { | ||
// On limited API or with refcount debugging, let the interpreter do refcounting | ||
// On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting | ||
// On 3.12+ we implement refcount debugging to get better assertion locations on negative refcounts | ||
// TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. | ||
#[cfg(any( | ||
Py_GIL_DISABLED, | ||
Py_LIMITED_API, | ||
all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), | ||
GraalPy | ||
|
@@ -580,6 +642,7 @@ pub unsafe fn Py_DECREF(op: *mut PyObject) { | |
} | ||
|
||
#[cfg(not(any( | ||
Py_GIL_DISABLED, | ||
Py_LIMITED_API, | ||
all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), | ||
GraalPy | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very cool to see this job running, thanks 🙏