Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
gh-117511: Make PyMutex public in the non-limited API#117731
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
3c743f8d340d28e59e0f71cef88028c90bce3218176533089215020ac0f75f94ebc5e519a349272301346b2d377476e493File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -55,6 +55,11 @@ The following functions can be safely called before Python is initialized: | ||
| * :c:func:`PyMem_RawCalloc` | ||
| * :c:func:`PyMem_RawFree` | ||
| * Synchronization: | ||
| * :c:func:`PyMutex_Lock` | ||
| * :c:func:`PyMutex_Unlock` | ||
| .. note:: | ||
| The following functions **should not be called** before | ||
| @@ -2152,3 +2157,41 @@ be used in new code. | ||
| .. c:function:: void PyThread_delete_key_value(int key) | ||
| .. c:function:: void PyThread_ReInitTLS() | ||
| Synchronization Primitives | ||
| ========================== | ||
| The C-API provides a basic mutual exclusion lock. | ||
| .. c:type:: PyMutex | ||
| A mutual exclusion lock. The :c:type:`!PyMutex` should be initialized to | ||
| zero to represent the unlocked state. For example:: | ||
| PyMutex mutex = {0}; | ||
| Instances of :c:type:`!PyMutex` should not be copied or moved. Both the | ||
| contents and address of a :c:type:`!PyMutex` are meaningful, and it must | ||
| remain at a fixed, writable location in memory. | ||
| .. note:: | ||
| A :c:type:`!PyMutex` currently occupies one byte, but the size should be | ||
| considered unstable. The size may change in future Python releases | ||
| without a deprecation period. | ||
| .. versionadded:: 3.13 | ||
| .. c:function:: void PyMutex_Lock(PyMutex *m) | ||
| Lock mutex *m*. If another thread has already locked it, the calling | ||
| thread will block until the mutex is unlocked. While blocked, the thread | ||
| will temporarily release the :term:`GIL` if it is held. | ||
| .. versionadded:: 3.13 | ||
| .. c:function:: void PyMutex_Unlock(PyMutex *m) | ||
| Unlock mutex *m*. The mutex must be locked --- otherwise, the function will | ||
| issue a fatal error. | ||
vstinner marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| .. versionadded:: 3.13 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| #ifndef Py_CPYTHON_LOCK_H | ||
| # error "this header file must not be included directly" | ||
| #endif | ||
| #define _Py_UNLOCKED 0 | ||
| #define _Py_LOCKED 1 | ||
| // A mutex that occupies one byte. The lock can be zero initialized to | ||
| // represent the unlocked state. | ||
| // | ||
| // Typical initialization: | ||
| // PyMutex m = (PyMutex){0}; | ||
| // | ||
| // Or initialize as global variables: | ||
| // static PyMutex m; | ||
| // | ||
| // Typical usage: | ||
| // PyMutex_Lock(&m); | ||
| // ... | ||
| // PyMutex_Unlock(&m); | ||
| // | ||
| // The contents of the PyMutex are not part of the public API, but are | ||
| // described to aid in understanding the implementation and debugging. Only | ||
| // the two least significant bits are used. The remaining bits are always zero: | ||
| // 0b00: unlocked | ||
| // 0b01: locked | ||
| // 0b10: unlocked and has parked threads | ||
| // 0b11: locked and has parked threads | ||
| typedef struct PyMutex { | ||
| uint8_t _bits; // (private) | ||
| } PyMutex; | ||
| // exported function for locking the mutex | ||
| PyAPI_FUNC(void) PyMutex_Lock(PyMutex *m); | ||
| // exported function for unlocking the mutex | ||
| PyAPI_FUNC(void) PyMutex_Unlock(PyMutex *m); | ||
| // Locks the mutex. | ||
| // | ||
| // If the mutex is currently locked, the calling thread will be parked until | ||
| // the mutex is unlocked. If the current thread holds the GIL, then the GIL | ||
| // will be released while the thread is parked. | ||
| static inline void | ||
| _PyMutex_Lock(PyMutex *m) | ||
| { | ||
| uint8_t expected = _Py_UNLOCKED; | ||
| if (!_Py_atomic_compare_exchange_uint8(&m->_bits, &expected, _Py_LOCKED)) { | ||
| PyMutex_Lock(m); | ||
colesbury marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| } | ||
| #define PyMutex_Lock _PyMutex_Lock | ||
| // Unlocks the mutex. | ||
| static inline void | ||
| _PyMutex_Unlock(PyMutex *m) | ||
| { | ||
| uint8_t expected = _Py_LOCKED; | ||
| if (!_Py_atomic_compare_exchange_uint8(&m->_bits, &expected, _Py_UNLOCKED)) { | ||
| PyMutex_Unlock(m); | ||
| } | ||
| } | ||
| #define PyMutex_Unlock _PyMutex_Unlock | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| #ifndef Py_LOCK_H | ||
| #define Py_LOCK_H | ||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
| #ifndef Py_LIMITED_API | ||
| # define Py_CPYTHON_LOCK_H | ||
| # include "cpython/lock.h" | ||
Member 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 don't see the value of such header file. Just include cpython/lock.h in Python.h, and check Py_LIMITED_API in cpython/lock.h. ContributorAuthor 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. @ericsnowcurrently expressed a preference for this style when Member 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. IMO, it's helpful to follow a consistent pattern when it comes to the Include/cpython header files. That means in some cases we end up with very minimal header files like this in Include/. Member 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. If you mention consistency, there are already many header files in Include/cpython/ which have no companion Include/ header file: | ||
| # undef Py_CPYTHON_LOCK_H | ||
| #endif | ||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
| #endif /* !Py_LOCK_H */ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Make the :c:type:`PyMutex` public in the non-limited C API. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -2372,7 +2372,7 @@ new_reference(PyObject *op) | ||
| #else | ||
| op->ob_tid = _Py_ThreadId(); | ||
| op->_padding = 0; | ||
| op->ob_mutex = (struct _PyMutex){ 0 }; | ||
| op->ob_mutex = (PyMutex){ 0 }; | ||
Member 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. If there is no public PyMutex_STATIC_INIT, can you maybe add a private one in pycore_lock.h? ContributorAuthor 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. Let's consider that in a separate PR:
| ||
| op->ob_gc_bits = 0; | ||
| op->ob_ref_local = 1; | ||
| op->ob_ref_shared = 0; | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.