This commit is contained in:
Thomas Wouters 2024-02-15 21:53:06 +01:00
commit 26f23daa1e
38 changed files with 1321 additions and 139 deletions

View file

@ -469,6 +469,9 @@ _Py_atomic_store_int_release(int *obj, int value);
static inline int
_Py_atomic_load_int_acquire(const int *obj);
static inline uint32_t
_Py_atomic_load_uint32_acquire(const uint32_t *obj);
// --- _Py_atomic_fence ------------------------------------------------------

View file

@ -495,6 +495,9 @@ static inline int
_Py_atomic_load_int_acquire(const int *obj)
{ return __atomic_load_n(obj, __ATOMIC_ACQUIRE); }
static inline uint32_t
_Py_atomic_load_uint32_acquire(const uint32_t *obj)
{ return __atomic_load_n(obj, __ATOMIC_ACQUIRE); }
// --- _Py_atomic_fence ------------------------------------------------------

View file

@ -938,6 +938,17 @@ _Py_atomic_load_int_acquire(const int *obj)
#endif
}
static inline uint32_t
_Py_atomic_load_uint32_acquire(const uint32_t *obj)
{
#if defined(_M_X64) || defined(_M_IX86)
return *(uint32_t volatile *)obj;
#elif defined(_M_ARM64)
return (int)__ldar32((uint32_t volatile *)obj);
#else
# error "no implementation of _Py_atomic_load_uint32_acquire"
#endif
}
// --- _Py_atomic_fence ------------------------------------------------------

View file

@ -870,6 +870,13 @@ _Py_atomic_load_int_acquire(const int *obj)
memory_order_acquire);
}
static inline uint32_t
_Py_atomic_load_uint32_acquire(const uint32_t *obj)
{
_Py_USING_STD;
return atomic_load_explicit((const _Atomic(uint32_t)*)obj,
memory_order_acquire);
}
// --- _Py_atomic_fence ------------------------------------------------------

View file

@ -96,6 +96,15 @@ extern "C" {
_PyCriticalSection_End(&_cs); \
}
# define Py_XBEGIN_CRITICAL_SECTION(op) \
{ \
_PyCriticalSection _cs_opt = {0}; \
_PyCriticalSection_XBegin(&_cs_opt, _PyObject_CAST(op))
# define Py_XEND_CRITICAL_SECTION() \
_PyCriticalSection_XEnd(&_cs_opt); \
}
# define Py_BEGIN_CRITICAL_SECTION2(a, b) \
{ \
_PyCriticalSection2 _cs2; \
@ -131,6 +140,8 @@ extern "C" {
// The critical section APIs are no-ops with the GIL.
# define Py_BEGIN_CRITICAL_SECTION(op)
# define Py_END_CRITICAL_SECTION()
# define Py_XBEGIN_CRITICAL_SECTION(op)
# define Py_XEND_CRITICAL_SECTION()
# define Py_BEGIN_CRITICAL_SECTION2(a, b)
# define Py_END_CRITICAL_SECTION2()
# define _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(mutex)
@ -187,6 +198,16 @@ _PyCriticalSection_Begin(_PyCriticalSection *c, PyMutex *m)
}
}
static inline void
_PyCriticalSection_XBegin(_PyCriticalSection *c, PyObject *op)
{
#ifdef Py_GIL_DISABLED
if (op != NULL) {
_PyCriticalSection_Begin(c, &_PyObject_CAST(op)->ob_mutex);
}
#endif
}
// Removes the top-most critical section from the thread's stack of critical
// sections. If the new top-most critical section is inactive, then it is
// resumed.
@ -209,6 +230,14 @@ _PyCriticalSection_End(_PyCriticalSection *c)
_PyCriticalSection_Pop(c);
}
static inline void
_PyCriticalSection_XEnd(_PyCriticalSection *c)
{
if (c->mutex) {
_PyCriticalSection_End(c);
}
}
static inline void
_PyCriticalSection2_Begin(_PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2)
{
@ -264,7 +293,8 @@ _PyCriticalSection_SuspendAll(PyThreadState *tstate);
#ifdef Py_GIL_DISABLED
static inline void
_PyCriticalSection_AssertHeld(PyMutex *mutex) {
_PyCriticalSection_AssertHeld(PyMutex *mutex)
{
#ifdef Py_DEBUG
PyThreadState *tstate = _PyThreadState_GET();
uintptr_t prev = tstate->critical_section;

View file

@ -742,6 +742,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_abc_impl));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_abstract_));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_active));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_align_));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_annotation));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_anonymous_));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_argtypes_));

View file

@ -231,6 +231,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(_abc_impl)
STRUCT_FOR_ID(_abstract_)
STRUCT_FOR_ID(_active)
STRUCT_FOR_ID(_align_)
STRUCT_FOR_ID(_annotation)
STRUCT_FOR_ID(_anonymous_)
STRUCT_FOR_ID(_argtypes_)

View file

@ -251,6 +251,39 @@ PyAPI_FUNC(void) _PyRWMutex_RUnlock(_PyRWMutex *rwmutex);
PyAPI_FUNC(void) _PyRWMutex_Lock(_PyRWMutex *rwmutex);
PyAPI_FUNC(void) _PyRWMutex_Unlock(_PyRWMutex *rwmutex);
// Similar to linux seqlock: https://en.wikipedia.org/wiki/Seqlock
// We use a sequence number to lock the writer, an even sequence means we're unlocked, an odd
// sequence means we're locked. Readers will read the sequence before attempting to read the
// underlying data and then read the sequence number again after reading the data. If the
// sequence has not changed the data is valid.
//
// Differs a little bit in that we use CAS on sequence as the lock, instead of a seperate spin lock.
// The writer can also detect that the undelering data has not changed and abandon the write
// and restore the previous sequence.
typedef struct {
uint32_t sequence;
} _PySeqLock;
// Lock the sequence lock for the writer
PyAPI_FUNC(void) _PySeqLock_LockWrite(_PySeqLock *seqlock);
// Unlock the sequence lock and move to the next sequence number.
PyAPI_FUNC(void) _PySeqLock_UnlockWrite(_PySeqLock *seqlock);
// Abandon the current update indicating that no mutations have occured
// and restore the previous sequence value.
PyAPI_FUNC(void) _PySeqLock_AbandonWrite(_PySeqLock *seqlock);
// Begin a read operation and return the current sequence number.
PyAPI_FUNC(uint32_t) _PySeqLock_BeginRead(_PySeqLock *seqlock);
// End the read operation and confirm that the sequence number has not changed.
// Returns 1 if the read was successful or 0 if the read should be re-tried.
PyAPI_FUNC(uint32_t) _PySeqLock_EndRead(_PySeqLock *seqlock, uint32_t previous);
// Check if the lock was held during a fork and clear the lock. Returns 1
// if the lock was held and any associated datat should be cleared.
PyAPI_FUNC(uint32_t) _PySeqLock_AfterFork(_PySeqLock *seqlock);
#ifdef __cplusplus
}

View file

@ -740,6 +740,7 @@ extern "C" {
INIT_ID(_abc_impl), \
INIT_ID(_abstract_), \
INIT_ID(_active), \
INIT_ID(_align_), \
INIT_ID(_annotation), \
INIT_ID(_anonymous_), \
INIT_ID(_argtypes_), \

View file

@ -9,6 +9,7 @@ extern "C" {
#endif
#include "pycore_moduleobject.h" // PyModuleObject
#include "pycore_lock.h" // PyMutex
/* state */
@ -21,6 +22,7 @@ struct _types_runtime_state {
// bpo-42745: next_version_tag remains shared by all interpreters
// because of static types.
unsigned int next_version_tag;
PyMutex type_mutex;
};
@ -28,6 +30,9 @@ struct _types_runtime_state {
// see _PyType_Lookup().
struct type_cache_entry {
unsigned int version; // initialized from type->tp_version_tag
#ifdef Py_GIL_DISABLED
_PySeqLock sequence;
#endif
PyObject *name; // reference to exactly a str or None
PyObject *value; // borrowed reference or NULL
};
@ -74,7 +79,7 @@ struct types_state {
extern PyStatus _PyTypes_InitTypes(PyInterpreterState *);
extern void _PyTypes_FiniTypes(PyInterpreterState *);
extern void _PyTypes_Fini(PyInterpreterState *);
extern void _PyTypes_AfterFork(void);
/* other API */

View file

@ -534,6 +534,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(_active);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(_align_);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(_annotation);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);