pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/3af7263037de1d0ef63b070fc7bfc2cf042eaebe

ff8eaaaff3a3.css" /> gh-117511: Make PyMutex public in the non-limited API (#117731) · python/cpython@3af7263 · GitHub
Skip to content

Commit 3af7263

Browse files
authored
gh-117511: Make PyMutex public in the non-limited API (#117731)
1 parent e8e151d commit 3af7263

File tree

18 files changed

+185
-110
lines changed

18 files changed

+185
-110
lines changed

Doc/c-api/init.rst

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ The following functions can be safely called before Python is initialized:
5555
* :c:func:`PyMem_RawCalloc`
5656
* :c:func:`PyMem_RawFree`
5757

58+
* Synchronization:
59+
60+
* :c:func:`PyMutex_Lock`
61+
* :c:func:`PyMutex_Unlock`
62+
5863
.. note::
5964

6065
The following functions **should not be called** before
@@ -2152,3 +2157,41 @@ be used in new code.
21522157
.. c:function:: void PyThread_delete_key_value(int key)
21532158
.. c:function:: void PyThread_ReInitTLS()
21542159
2160+
Synchronization Primitives
2161+
==========================
2162+
2163+
The C-API provides a basic mutual exclusion lock.
2164+
2165+
.. c:type:: PyMutex
2166+
2167+
A mutual exclusion lock. The :c:type:`!PyMutex` should be initialized to
2168+
zero to represent the unlocked state. For example::
2169+
2170+
PyMutex mutex = {0};
2171+
2172+
Instances of :c:type:`!PyMutex` should not be copied or moved. Both the
2173+
contents and address of a :c:type:`!PyMutex` are meaningful, and it must
2174+
remain at a fixed, writable location in memory.
2175+
2176+
.. note::
2177+
2178+
A :c:type:`!PyMutex` currently occupies one byte, but the size should be
2179+
considered unstable. The size may change in future Python releases
2180+
without a deprecation period.
2181+
2182+
.. versionadded:: 3.13
2183+
2184+
.. c:function:: void PyMutex_Lock(PyMutex *m)
2185+
2186+
Lock mutex *m*. If another thread has already locked it, the calling
2187+
thread will block until the mutex is unlocked. While blocked, the thread
2188+
will temporarily release the :term:`GIL` if it is held.
2189+
2190+
.. versionadded:: 3.13
2191+
2192+
.. c:function:: void PyMutex_Unlock(PyMutex *m)
2193+
2194+
Unlock mutex *m*. The mutex must be locked --- otherwise, the function will
2195+
issue a fatal error.
2196+
2197+
.. versionadded:: 3.13

Doc/whatsnew/3.13.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2172,6 +2172,11 @@ New Features
21722172
:c:func:`PyEval_GetLocals` return :term:`strong references <strong reference>`
21732173
rather than borrowed references. (Added as part of :pep:`667`.)
21742174

2175+
* Add :c:type:`PyMutex` API, a lightweight mutex that occupies a single byte.
2176+
The :c:func:`PyMutex_Lock` function will release the GIL (if currently held)
2177+
if the operation needs to block.
2178+
(Contributed by Sam Gross in :gh:`108724`.)
2179+
21752180
Build Changes
21762181
=============
21772182

Include/Python.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include "pybuffer.h"
6565
#include "pystats.h"
6666
#include "pyatomic.h"
67+
#include "lock.h"
6768
#include "object.h"
6869
#include "refcount.h"
6970
#include "objimpl.h"

Include/cpython/lock.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#ifndef Py_CPYTHON_LOCK_H
2+
# error "this header file must not be included directly"
3+
#endif
4+
5+
#define _Py_UNLOCKED 0
6+
#define _Py_LOCKED 1
7+
8+
// A mutex that occupies one byte. The lock can be zero initialized to
9+
// represent the unlocked state.
10+
//
11+
// Typical initialization:
12+
// PyMutex m = (PyMutex){0};
13+
//
14+
// Or initialize as global variables:
15+
// static PyMutex m;
16+
//
17+
// Typical usage:
18+
// PyMutex_Lock(&m);
19+
// ...
20+
// PyMutex_Unlock(&m);
21+
//
22+
// The contents of the PyMutex are not part of the public API, but are
23+
// described to aid in understanding the implementation and debugging. Only
24+
// the two least significant bits are used. The remaining bits are always zero:
25+
// 0b00: unlocked
26+
// 0b01: locked
27+
// 0b10: unlocked and has parked threads
28+
// 0b11: locked and has parked threads
29+
typedef struct PyMutex {
30+
uint8_t _bits; // (private)
31+
} PyMutex;
32+
33+
// exported function for locking the mutex
34+
PyAPI_FUNC(void) PyMutex_Lock(PyMutex *m);
35+
36+
// exported function for unlocking the mutex
37+
PyAPI_FUNC(void) PyMutex_Unlock(PyMutex *m);
38+
39+
// Locks the mutex.
40+
//
41+
// If the mutex is currently locked, the calling thread will be parked until
42+
// the mutex is unlocked. If the current thread holds the GIL, then the GIL
43+
// will be released while the thread is parked.
44+
static inline void
45+
_PyMutex_Lock(PyMutex *m)
46+
{
47+
uint8_t expected = _Py_UNLOCKED;
48+
if (!_Py_atomic_compare_exchange_uint8(&m->_bits, &expected, _Py_LOCKED)) {
49+
PyMutex_Lock(m);
50+
}
51+
}
52+
#define PyMutex_Lock _PyMutex_Lock
53+
54+
// Unlocks the mutex.
55+
static inline void
56+
_PyMutex_Unlock(PyMutex *m)
57+
{
58+
uint8_t expected = _Py_LOCKED;
59+
if (!_Py_atomic_compare_exchange_uint8(&m->_bits, &expected, _Py_UNLOCKED)) {
60+
PyMutex_Unlock(m);
61+
}
62+
}
63+
#define PyMutex_Unlock _PyMutex_Unlock

Include/cpython/weakrefobject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct _PyWeakReference {
3636
* Normally this can be derived from wr_object, but in some cases we need
3737
* to lock after wr_object has been set to Py_None.
3838
*/
39-
struct _PyMutex *weakrefs_lock;
39+
PyMutex *weakrefs_lock;
4040
#endif
4141
};
4242

Include/internal/pycore_critical_section.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ _PyCriticalSection2_BeginSlow(_PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2,
202202
static inline void
203203
_PyCriticalSection_Begin(_PyCriticalSection *c, PyMutex *m)
204204
{
205-
if (PyMutex_LockFast(&m->v)) {
205+
if (PyMutex_LockFast(&m->_bits)) {
206206
PyThreadState *tstate = _PyThreadState_GET();
207207
c->mutex = m;
208208
c->prev = tstate->critical_section;
@@ -255,8 +255,8 @@ _PyCriticalSection2_Begin(_PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
255255
m2 = tmp;
256256
}
257257

258-
if (PyMutex_LockFast(&m1->v)) {
259-
if (PyMutex_LockFast(&m2->v)) {
258+
if (PyMutex_LockFast(&m1->_bits)) {
259+
if (PyMutex_LockFast(&m2->_bits)) {
260260
PyThreadState *tstate = _PyThreadState_GET();
261261
c->base.mutex = m1;
262262
c->mutex2 = m2;

Include/internal/pycore_lock.h

Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13,84 +13,22 @@ extern "C" {
1313
# error "this header requires Py_BUILD_CORE define"
1414
#endif
1515

16-
17-
// A mutex that occupies one byte. The lock can be zero initialized.
18-
//
19-
// Only the two least significant bits are used. The remaining bits should be
20-
// zero:
21-
// 0b00: unlocked
22-
// 0b01: locked
23-
// 0b10: unlocked and has parked threads
24-
// 0b11: locked and has parked threads
25-
//
26-
// Typical initialization:
27-
// PyMutex m = (PyMutex){0};
28-
//
29-
// Or initialize as global variables:
30-
// static PyMutex m;
31-
//
32-
// Typical usage:
33-
// PyMutex_Lock(&m);
34-
// ...
35-
// PyMutex_Unlock(&m);
36-
37-
// NOTE: In Py_GIL_DISABLED builds, `struct _PyMutex` is defined in Include/object.h.
38-
// The Py_GIL_DISABLED builds need the definition in Include/object.h for the
39-
// `ob_mutex` field in PyObject. For the default (non-free-threaded) build,
40-
// we define the struct here to avoid exposing it in the public API.
41-
#ifndef Py_GIL_DISABLED
42-
struct _PyMutex { uint8_t v; };
43-
#endif
44-
45-
typedef struct _PyMutex PyMutex;
46-
47-
#define _Py_UNLOCKED 0
48-
#define _Py_LOCKED 1
16+
//_Py_UNLOCKED is defined as 0 and _Py_LOCKED as 1 in Include/cpython/lock.h
4917
#define _Py_HAS_PARKED 2
5018
#define _Py_ONCE_INITIALIZED 4
5119

52-
// (private) slow path for locking the mutex
53-
PyAPI_FUNC(void) _PyMutex_LockSlow(PyMutex *m);
54-
55-
// (private) slow path for unlocking the mutex
56-
PyAPI_FUNC(void) _PyMutex_UnlockSlow(PyMutex *m);
57-
5820
static inline int
5921
PyMutex_LockFast(uint8_t *lock_bits)
6022
{
6123
uint8_t expected = _Py_UNLOCKED;
6224
return _Py_atomic_compare_exchange_uint8(lock_bits, &expected, _Py_LOCKED);
6325
}
6426

65-
// Locks the mutex.
66-
//
67-
// If the mutex is currently locked, the calling thread will be parked until
68-
// the mutex is unlocked. If the current thread holds the GIL, then the GIL
69-
// will be released while the thread is parked.
70-
static inline void
71-
PyMutex_Lock(PyMutex *m)
72-
{
73-
uint8_t expected = _Py_UNLOCKED;
74-
if (!_Py_atomic_compare_exchange_uint8(&m->v, &expected, _Py_LOCKED)) {
75-
_PyMutex_LockSlow(m);
76-
}
77-
}
78-
79-
// Unlocks the mutex.
80-
static inline void
81-
PyMutex_Unlock(PyMutex *m)
82-
{
83-
uint8_t expected = _Py_LOCKED;
84-
if (!_Py_atomic_compare_exchange_uint8(&m->v, &expected, _Py_UNLOCKED)) {
85-
_PyMutex_UnlockSlow(m);
86-
}
87-
}
88-
8927
// Checks if the mutex is currently locked.
9028
static inline int
9129
PyMutex_IsLocked(PyMutex *m)
9230
{
93-
return (_Py_atomic_load_uint8(&m->v) & _Py_LOCKED) != 0;
31+
return (_Py_atomic_load_uint8(&m->_bits) & _Py_LOCKED) != 0;
9432
}
9533

9634
// Re-initializes the mutex after a fork to the unlocked state.
@@ -121,7 +59,7 @@ static inline void
12159
PyMutex_LockFlags(PyMutex *m, _PyLockFlags flags)
12260
{
12361
uint8_t expected = _Py_UNLOCKED;
124-
if (!_Py_atomic_compare_exchange_uint8(&m->v, &expected, _Py_LOCKED)) {
62+
if (!_Py_atomic_compare_exchange_uint8(&m->_bits, &expected, _Py_LOCKED)) {
12563
_PyMutex_LockTimed(m, -1, flags);
12664
}
12765
}

Include/internal/pycore_warnings.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct _warnings_runtime_state {
1414
PyObject *filters; /* List */
1515
PyObject *once_registry; /* Dict */
1616
PyObject *default_action; /* String */
17-
struct _PyMutex mutex;
17+
PyMutex mutex;
1818
long filters_version;
1919
};
2020

Include/lock.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef Py_LOCK_H
2+
#define Py_LOCK_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_LIMITED_API
8+
# define Py_CPYTHON_LOCK_H
9+
# include "cpython/lock.h"
10+
# undef Py_CPYTHON_LOCK_H
11+
#endif
12+
13+
#ifdef __cplusplus
14+
}
15+
#endif
16+
#endif /* !Py_LOCK_H */

Include/object.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,13 @@ struct _object {
137137
// fields have been merged.
138138
#define _Py_UNOWNED_TID 0
139139

140-
// NOTE: In non-free-threaded builds, `struct _PyMutex` is defined in
141-
// pycore_lock.h. See pycore_lock.h for more details.
142-
struct _PyMutex { uint8_t v; };
143-
144140
struct _object {
145141
// ob_tid stores the thread id (or zero). It is also used by the GC and the
146142
// trashcan mechanism as a linked list pointer and by the GC to store the
147143
// computed "gc_refs" refcount.
148144
uintptr_t ob_tid;
149145
uint16_t _padding;
150-
struct _PyMutex ob_mutex; // per-object lock
146+
PyMutex ob_mutex; // per-object lock
151147
uint8_t ob_gc_bits; // gc-related state
152148
uint32_t ob_ref_local; // local reference count
153149
Py_ssize_t ob_ref_shared; // shared (atomic) reference count

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy