Skip to content
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

gh-115432: Add critical section variant that handles a NULL object #115433

Merged
merged 4 commits into from
Feb 15, 2024

Conversation

colesbury
Copy link
Contributor

@colesbury colesbury commented Feb 13, 2024

This adds Py_XBEGIN_CRITICAL_SECTION and Py_XEND_CRITICAL_SECTION, which accept a possibly NULL object as an argument. If the argument is NULL, then nothing is locked or unlocked. Otherwise, they behave like Py_BEGIN/END_CRITICAL_SECTION.

This adds `Py_BEGIN_CRITICAL_SECTION_OPT` and
`Py_END_CRITICAL_SECTION_OPT`, which accept a possibly NULL object as an
argument. If the argument is NULL, then nothing is locked or unlocked.
Otherwise, they behave like `Py_BEGIN/END_CRITICAL_SECTION`.
@colesbury
Copy link
Contributor Author

cc @tomasr8

@colesbury
Copy link
Contributor Author

colesbury commented Feb 13, 2024

The context here is in the set implementation, we have a number of functions that take a possibly NULL iterable argument:

cpython/Objects/setobject.c

Lines 1015 to 1016 in 514b1c9

static PyObject *
make_new_set_basetype(PyTypeObject *type, PyObject *iterable)

We generally want to lock the iterable if it's not NULL. There's enough uses that I think it's worth handling in pycore_critical_section.h rather than in setobject.c.

Related PR: #113800

Comment on lines +204 to +208
#ifdef Py_GIL_DISABLED
if (op != NULL) {
_PyCriticalSection_Begin(c, &_PyObject_CAST(op)->ob_mutex);
}
#endif
Copy link
Member

Choose a reason for hiding this comment

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

Is the #ifdef needed here? The whole macro should be a no-op when the GIL is enabled

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's needed because of the &_PyObject_CAST(op)->ob_mutex will only compile on the free-threaded build. In the default build, PyObject does not have an ob_mutex field.

We could have instead put the #ifdef Py_GIL_DISABLED around the whole _PyCriticalSection_BeginOpt function declaration. That would work too.

Copy link
Member

Choose a reason for hiding this comment

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

Ah cool, thanks for clearing that up!

@DinoV
Copy link
Contributor

DinoV commented Feb 15, 2024

The context here is in the set implementation, we have a number of functions that take a possibly NULL iterable argument:

cpython/Objects/setobject.c

Lines 1015 to 1016 in 514b1c9

static PyObject *
make_new_set_basetype(PyTypeObject *type, PyObject *iterable)

I kind of wonder if we do really want to lock all of the iterables... Ultimately the dict case could proceed lock-free and for some arbitrary object that isn't a dict or a list we don't really need to lock it (e.g. for a Python used defined type it's presumably protected by some other Python lock if it needs to be thread safe).

But I could see this as being generally useful too.

@colesbury
Copy link
Contributor Author

I kind of wonder if we do really want to lock all of the iterables...

Yeah, I think we will want to refine the locking approach in the future, but locking all the iterables works for now. I think we want to actually lock in the dict case so that the whole operation appears atomic, even though the iteration over the dict is thread-safe without a lock. IIRC, we have code that assumes that set(dict) is atomic.

@colesbury colesbury merged commit ad4f909 into python:main Feb 15, 2024
35 checks passed
@colesbury colesbury deleted the gh-115432 branch February 15, 2024 13:37
diegorusso pushed a commit to diegorusso/cpython that referenced this pull request Apr 17, 2024
…ect (python#115433)

This adds `Py_XBEGIN_CRITICAL_SECTION` and
`Py_XEND_CRITICAL_SECTION`, which accept a possibly NULL object as an
argument. If the argument is NULL, then nothing is locked or unlocked.
Otherwise, they behave like `Py_BEGIN/END_CRITICAL_SECTION`.
colesbury added a commit to colesbury/cpython that referenced this pull request May 9, 2024
…NULL object (python#115433)"

This reverts commit ad4f909.

The API ended up not being used.
colesbury added a commit that referenced this pull request May 9, 2024
…bject (#115433)" (#118861)

This reverts commit ad4f909.

The API ended up not being used.
miss-islington pushed a commit to miss-islington/cpython that referenced this pull request May 9, 2024
…NULL object (pythonGH-115433)" (pythonGH-118861)

This reverts commit ad4f909.

The API ended up not being used.
(cherry picked from commit 46c8081)

Co-authored-by: Sam Gross <colesbury@gmail.com>
colesbury added a commit that referenced this pull request May 9, 2024
… NULL object (GH-115433)" (GH-118861) (#118872)

This reverts commit ad4f909.

The API ended up not being used.
(cherry picked from commit 46c8081)

Co-authored-by: Sam Gross <colesbury@gmail.com>
SonicField pushed a commit to SonicField/cpython that referenced this pull request May 10, 2024
…NULL object (python#115433)" (python#118861)

This reverts commit ad4f909.

The API ended up not being used.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants