-
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
Add support for nogil Python #2885
Changes from all commits
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 |
---|---|---|
|
@@ -19,6 +19,13 @@ pub const PyObject_HEAD_INIT: PyObject = PyObject { | |
_ob_next: std::ptr::null_mut(), | ||
#[cfg(py_sys_config = "Py_TRACE_REFS")] | ||
_ob_prev: std::ptr::null_mut(), | ||
#[cfg(Py_NOGIL)] | ||
ob_tid: 0, | ||
#[cfg(Py_NOGIL)] | ||
ob_reflocal: 1, | ||
#[cfg(Py_NOGIL)] | ||
ob_ref_shared: 0, | ||
#[cfg(not(Py_NOGIL))] | ||
ob_refcnt: 1, | ||
#[cfg(PyPy)] | ||
ob_pypy_link: 0, | ||
|
@@ -35,6 +42,13 @@ pub struct PyObject { | |
pub _ob_next: *mut PyObject, | ||
#[cfg(py_sys_config = "Py_TRACE_REFS")] | ||
pub _ob_prev: *mut PyObject, | ||
#[cfg(Py_NOGIL)] | ||
pub ob_tid: usize, | ||
#[cfg(Py_NOGIL)] | ||
pub ob_reflocal: u32, | ||
#[cfg(Py_NOGIL)] | ||
pub ob_ref_shared: u32, | ||
#[cfg(not(Py_NOGIL))] | ||
pub ob_refcnt: Py_ssize_t, | ||
#[cfg(PyPy)] | ||
pub ob_pypy_link: Py_ssize_t, | ||
|
@@ -62,11 +76,19 @@ pub unsafe fn Py_Is(x: *mut PyObject, y: *mut PyObject) -> c_int { | |
// skipped _Py_REFCNT: defined in Py_REFCNT | ||
|
||
#[inline] | ||
#[cfg(not(Py_NOGIL))] | ||
pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { | ||
assert!(!ob.is_null()); | ||
(*ob).ob_refcnt | ||
} | ||
|
||
#[inline] | ||
#[cfg(Py_NOGIL)] | ||
pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { | ||
assert!(!ob.is_null()); | ||
Py_RefCnt(ob) | ||
} | ||
|
||
#[inline] | ||
pub unsafe fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject { | ||
(*ob).ob_type | ||
|
@@ -409,26 +431,32 @@ extern "C" { | |
|
||
// Reference counting macros. | ||
#[inline] | ||
#[cfg(not(any(py_sys_config = "Py_REF_DEBUG", Py_NOGIL)))] | ||
pub unsafe fn Py_INCREF(op: *mut PyObject) { | ||
if cfg!(py_sys_config = "Py_REF_DEBUG") { | ||
Py_IncRef(op) | ||
} else { | ||
(*op).ob_refcnt += 1 | ||
} | ||
(*op).ob_refcnt += 1 | ||
} | ||
|
||
#[inline] | ||
#[cfg(any(py_sys_config = "Py_REF_DEBUG", Py_NOGIL))] | ||
pub unsafe fn Py_INCREF(op: *mut PyObject) { | ||
Py_IncRef(op) | ||
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. @colesbury - I understood that for the CPython stable API there was a discussion about making refcounting details internal to CPython but it was deemed infeasible due to performance regression. Here this patch seems to do exactly that. Are you able to comment on the estimated performance impact for extensions by changing refcounting to be through an FFI call? 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 think I have heard something similar, but I can't find the original source and don't know for which extensions it was deemed too great a cost. There didn't seem to be an noticeable performance impact for the cryptography extension, but I don't know enough to make any general estimates. |
||
} | ||
|
||
#[inline] | ||
#[cfg(not(any(py_sys_config = "Py_REF_DEBUG", Py_NOGIL)))] | ||
pub unsafe fn Py_DECREF(op: *mut PyObject) { | ||
if cfg!(py_sys_config = "Py_REF_DEBUG") { | ||
Py_DecRef(op) | ||
} else { | ||
(*op).ob_refcnt -= 1; | ||
if (*op).ob_refcnt == 0 { | ||
_Py_Dealloc(op) | ||
} | ||
(*op).ob_refcnt -= 1; | ||
if (*op).ob_refcnt == 0 { | ||
_Py_Dealloc(op) | ||
} | ||
} | ||
|
||
#[inline] | ||
#[cfg(any(py_sys_config = "Py_REF_DEBUG", Py_NOGIL))] | ||
pub unsafe fn Py_DECREF(op: *mut PyObject) { | ||
Py_DecRef(op) | ||
} | ||
|
||
#[inline] | ||
pub unsafe fn Py_CLEAR(op: *mut *mut PyObject) { | ||
let tmp = *op; | ||
|
@@ -457,6 +485,8 @@ extern "C" { | |
pub fn Py_IncRef(o: *mut PyObject); | ||
#[cfg_attr(PyPy, link_name = "PyPy_DecRef")] | ||
pub fn Py_DecRef(o: *mut PyObject); | ||
#[cfg(Py_NOGIL)] | ||
pub fn Py_RefCnt(o: *mut PyObject) -> Py_ssize_t; | ||
|
||
#[cfg(Py_3_10)] | ||
pub fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject; | ||
|
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.
TBH, adding these provisional APIs and changing PyO3 to use them is a 👎 from me, the callsites which you modified should already be converting borrowed references to owned, and if they didn't, that's a separate bug.EDIT: I reread the PEP and see now that it's necessary for thread-safety that the borrowed-to-owned conversion is done within the interpreter rather than PyO3. So I guess this would indeed be necessary.
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.
The idea behind this proposed change is that, when running without the GIL, the retrieval from list and the acquisition of an owned reference needs to be performed atomically in relation to any concurrent modifications. The PEP 703 proposes
PyList_FetchItem
andPyDict_GetItem
which have this behavior -- they return owned references and are safe in the face of concurrent modification.