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

Make Py_BEGIN_CRITICAL_SECTION() and Py_END_CRITICAL_SECTION() public in the non-limited C API #119344

Closed
colesbury opened this issue May 21, 2024 · 2 comments
Labels

Comments

@colesbury
Copy link
Contributor

colesbury commented May 21, 2024

Feature or enhancement

The critical section API is useful for making C API extensions thread-safe when the GIL is disabled. Compared to plain mutexes, the API makes it easier to avoid deadlocks, especially when interacting with the Python C API, where calls like Py_DECREF() may invoke destructors that themselves acquire locks.

A more detailed description and motivation is in PEP 703: https://peps.python.org/pep-0703/#python-critical-sections

The underlying implementation is hooked into Python's PyThreadState management, so it would not be practical to implement this API outside of CPython.

C-API WG Issue

Linked PRs

@da-woods
Copy link
Contributor

da-woods commented Jun 2, 2024

I've been trying to use these in Cython (by picking them out of the internal headers in the short term). One comment:

In a few places we end up wanting to have an error path and a non-error path out of the critical section (with both paths unlocking the critical section). That doesn't end up easy to do because the macros add brackets, so you can't have 1 BEGIN and 2 ENDs.

For the similar Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS there are equivalent bracket-less macros that make that easier to do (Py_UNBLOCK_THREADS and Py_BLOCK_THREADS).

Would it be possible to have something similar here?

(Edit: I can work around this though, but it might be nicer not to have to do so)

@colesbury
Copy link
Contributor Author

We can add another pair of macros like Py_[UN]BLOCK_THREADS, but the naming starts to get confusing. In CPython, we've generally factored things so as not to need that, either by moving things to separate functions (most commonly) or using goto or similar (less commonly).

colesbury added a commit to colesbury/cpython that referenced this issue Jun 20, 2024
colesbury added a commit that referenced this issue Jun 21, 2024
This makes the following macros public as part of the non-limited C-API for
locking a single object or two objects at once.

* `Py_BEGIN_CRITICAL_SECTION(op)` / `Py_END_CRITICAL_SECTION()`
* `Py_BEGIN_CRITICAL_SECTION2(a, b)` / `Py_END_CRITICAL_SECTION2()`

The supporting functions and structs used by the macros are also exposed for
cases where C macros are not available.
colesbury added a commit to colesbury/cpython that referenced this issue Jun 21, 2024
…9353)

This makes the following macros public as part of the non-limited C-API for
locking a single object or two objects at once.

* `Py_BEGIN_CRITICAL_SECTION(op)` / `Py_END_CRITICAL_SECTION()`
* `Py_BEGIN_CRITICAL_SECTION2(a, b)` / `Py_END_CRITICAL_SECTION2()`

The supporting functions and structs used by the macros are also exposed for
cases where C macros are not available.
(cherry picked from commit 8f17d69)

Co-authored-by: Sam Gross <colesbury@gmail.com>
colesbury added a commit that referenced this issue Jun 21, 2024
This makes the following macros public as part of the non-limited C-API for
locking a single object or two objects at once.

* `Py_BEGIN_CRITICAL_SECTION(op)` / `Py_END_CRITICAL_SECTION()`
* `Py_BEGIN_CRITICAL_SECTION2(a, b)` / `Py_END_CRITICAL_SECTION2()`

The supporting functions and structs used by the macros are also exposed for
cases where C macros are not available.
(cherry picked from commit 8f17d69)
mrahtz pushed a commit to mrahtz/cpython that referenced this issue Jun 30, 2024
This makes the following macros public as part of the non-limited C-API for
locking a single object or two objects at once.

* `Py_BEGIN_CRITICAL_SECTION(op)` / `Py_END_CRITICAL_SECTION()`
* `Py_BEGIN_CRITICAL_SECTION2(a, b)` / `Py_END_CRITICAL_SECTION2()`

The supporting functions and structs used by the macros are also exposed for
cases where C macros are not available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants
@colesbury @da-woods and others