Skip to content

gh-117657: Fix data races when writing / reading ob_gc_bits #118292

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

Merged
merged 4 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Include/cpython/pyatomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,14 @@ static inline uintptr_t
_Py_atomic_and_uintptr(uintptr_t *obj, uintptr_t value);


// --- _Py_atomic_and_relaxed ------------------------------------------------
// Performs `*obj &= value` atomically and returns the previous value of `*obj`
// (relaxed consistency).

static inline uint8_t
_Py_atomic_and_uint8_relaxed(uint8_t *obj, uint8_t value);


// --- _Py_atomic_or ---------------------------------------------------------
// Performs `*obj |= value` atomically and returns the previous value of `*obj`.

Expand All @@ -269,6 +277,13 @@ static inline uintptr_t
_Py_atomic_or_uintptr(uintptr_t *obj, uintptr_t value);


// --- _Py_atomic_or_relaxed -------------------------------------------------
// Performs `*obj |= value` atomically and returns the previous value of `*obj`
// (relaxed consistency).

static inline uint8_t
_Py_atomic_or_uint8_relaxed(uint8_t *obj, uint8_t value);

// --- _Py_atomic_load -------------------------------------------------------
// Atomically loads `*obj` (sequential consistency)

Expand Down
16 changes: 16 additions & 0 deletions Include/cpython/pyatomic_gcc.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ _Py_atomic_and_uintptr(uintptr_t *obj, uintptr_t value)
{ return __atomic_fetch_and(obj, value, __ATOMIC_SEQ_CST); }


// --- _Py_atomic_and_relaxed ------------------------------------------------

static inline uint8_t
_Py_atomic_and_uint8_relaxed(uint8_t *obj, uint8_t value)
{ return __atomic_fetch_and(obj, value, __ATOMIC_RELAXED); }


// --- _Py_atomic_or ---------------------------------------------------------

static inline uint8_t
Expand All @@ -241,6 +248,15 @@ _Py_atomic_or_uintptr(uintptr_t *obj, uintptr_t value)
{ return __atomic_fetch_or(obj, value, __ATOMIC_SEQ_CST); }


// --- _Py_atomic_or_relaxed -------------------------------------------------

static inline uint8_t
_Py_atomic_or_uint8_relaxed(uint8_t *obj, uint8_t value)
{
return __atomic_fetch_or(obj, value, __ATOMIC_RELAXED);
}


// --- _Py_atomic_load -------------------------------------------------------

static inline int
Expand Down
21 changes: 20 additions & 1 deletion Include/cpython/pyatomic_msc.h
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,16 @@ _Py_atomic_and_uintptr(uintptr_t *obj, uintptr_t value)
}


// --- _Py_atomic_and_relaxed ------------------------------------------------

static inline uint8_t
_Py_atomic_and_uint8_relaxed(uint8_t *obj, uint8_t value)
{
_Py_atomic_ASSERT_ARG_TYPE(char);
return (uint8_t)_InterlockedAnd8NoFence((volatile char *)obj, (char)value);
}


// --- _Py_atomic_or ---------------------------------------------------------

static inline uint8_t
Expand Down Expand Up @@ -490,7 +500,6 @@ _Py_atomic_or_uint64(uint64_t *obj, uint64_t value)
#endif
}


static inline uintptr_t
_Py_atomic_or_uintptr(uintptr_t *obj, uintptr_t value)
{
Expand All @@ -506,6 +515,16 @@ _Py_atomic_or_uintptr(uintptr_t *obj, uintptr_t value)
}


// --- _Py_atomic_or_relaxed -------------------------------------------------

static inline uint8_t
_Py_atomic_or_uint8_relaxed(uint8_t *obj, uint8_t value)
{
_Py_atomic_ASSERT_ARG_TYPE(char);
return (uint8_t)_InterlockedOr8NoFence((volatile char *)obj, (char)value);
}


// --- _Py_atomic_load -------------------------------------------------------

static inline uint8_t
Expand Down
20 changes: 20 additions & 0 deletions Include/cpython/pyatomic_std.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,16 @@ _Py_atomic_and_uintptr(uintptr_t *obj, uintptr_t value)
}


// --- _Py_atomic_and_relaxed ------------------------------------------------

static inline uint8_t
_Py_atomic_and_uint8_relaxed(uint8_t *obj, uint8_t value)
{
_Py_USING_STD;
return atomic_fetch_and_explicit((_Atomic(uint8_t)*)obj, value, memory_order_relaxed);
}


// --- _Py_atomic_or ---------------------------------------------------------

static inline uint8_t
Expand Down Expand Up @@ -404,6 +414,16 @@ _Py_atomic_or_uintptr(uintptr_t *obj, uintptr_t value)
}


// --- _Py_atomic_or_relaxed -------------------------------------------------

static inline uint8_t
_Py_atomic_or_uint8_relaxed(uint8_t *obj, uint8_t value)
{
_Py_USING_STD;
return atomic_fetch_or_explicit((_Atomic(uint8_t)*)obj, value, memory_order_relaxed);
}


// --- _Py_atomic_load -------------------------------------------------------

static inline int
Expand Down
38 changes: 30 additions & 8 deletions Include/internal/pycore_gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,32 @@ static inline PyObject* _Py_FROM_GC(PyGC_Head *gc) {
# define _PyGC_BITS_DEFERRED (64) // Use deferred reference counting
#endif

#ifdef Py_GIL_DISABLED

static inline void
_PyObject_SET_GC_BITS(PyObject *op, uint8_t bits)
{
_Py_atomic_or_uint8_relaxed(&op->ob_gc_bits, bits);
}

static inline int
_PyObject_HAS_GC_BITS(PyObject *op, uint8_t bits)
{
return (_Py_atomic_load_uint8_relaxed(&op->ob_gc_bits) & bits) != 0;
}

static inline void
_PyObject_CLEAR_GC_BITS(PyObject *op, uint8_t bits)
{
_Py_atomic_and_uint8_relaxed(&op->ob_gc_bits, ~bits);
}

#endif

/* True if the object is currently tracked by the GC. */
static inline int _PyObject_GC_IS_TRACKED(PyObject *op) {
#ifdef Py_GIL_DISABLED
return (op->ob_gc_bits & _PyGC_BITS_TRACKED) != 0;
return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_TRACKED);
#else
PyGC_Head *gc = _Py_AS_GC(op);
return (gc->_gc_next != 0);
Expand Down Expand Up @@ -80,12 +102,12 @@ static inline int _PyObject_GC_MAY_BE_TRACKED(PyObject *obj) {
* for calling _PyMem_FreeDelayed on the referenced
* memory. */
static inline int _PyObject_GC_IS_SHARED(PyObject *op) {
return (op->ob_gc_bits & _PyGC_BITS_SHARED) != 0;
return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_SHARED);
}
#define _PyObject_GC_IS_SHARED(op) _PyObject_GC_IS_SHARED(_Py_CAST(PyObject*, op))

static inline void _PyObject_GC_SET_SHARED(PyObject *op) {
op->ob_gc_bits |= _PyGC_BITS_SHARED;
_PyObject_SET_GC_BITS(op, _PyGC_BITS_SHARED);
}
#define _PyObject_GC_SET_SHARED(op) _PyObject_GC_SET_SHARED(_Py_CAST(PyObject*, op))

Expand All @@ -95,13 +117,13 @@ static inline void _PyObject_GC_SET_SHARED(PyObject *op) {
* Objects with this bit that are GC objects will automatically
* delay-freed by PyObject_GC_Del. */
static inline int _PyObject_GC_IS_SHARED_INLINE(PyObject *op) {
return (op->ob_gc_bits & _PyGC_BITS_SHARED_INLINE) != 0;
return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_SHARED_INLINE);
}
#define _PyObject_GC_IS_SHARED_INLINE(op) \
_PyObject_GC_IS_SHARED_INLINE(_Py_CAST(PyObject*, op))

static inline void _PyObject_GC_SET_SHARED_INLINE(PyObject *op) {
op->ob_gc_bits |= _PyGC_BITS_SHARED_INLINE;
_PyObject_SET_GC_BITS(op, _PyGC_BITS_SHARED_INLINE);
}
#define _PyObject_GC_SET_SHARED_INLINE(op) \
_PyObject_GC_SET_SHARED_INLINE(_Py_CAST(PyObject*, op))
Expand Down Expand Up @@ -178,23 +200,23 @@ static inline void _PyGCHead_SET_PREV(PyGC_Head *gc, PyGC_Head *prev) {

static inline int _PyGC_FINALIZED(PyObject *op) {
#ifdef Py_GIL_DISABLED
return (op->ob_gc_bits & _PyGC_BITS_FINALIZED) != 0;
return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_FINALIZED);
#else
PyGC_Head *gc = _Py_AS_GC(op);
return ((gc->_gc_prev & _PyGC_PREV_MASK_FINALIZED) != 0);
#endif
}
static inline void _PyGC_SET_FINALIZED(PyObject *op) {
#ifdef Py_GIL_DISABLED
op->ob_gc_bits |= _PyGC_BITS_FINALIZED;
_PyObject_SET_GC_BITS(op, _PyGC_BITS_FINALIZED);
#else
PyGC_Head *gc = _Py_AS_GC(op);
gc->_gc_prev |= _PyGC_PREV_MASK_FINALIZED;
#endif
}
static inline void _PyGC_CLEAR_FINALIZED(PyObject *op) {
#ifdef Py_GIL_DISABLED
op->ob_gc_bits &= ~_PyGC_BITS_FINALIZED;
_PyObject_CLEAR_GC_BITS(op, _PyGC_BITS_FINALIZED);
#else
PyGC_Head *gc = _Py_AS_GC(op);
gc->_gc_prev &= ~_PyGC_PREV_MASK_FINALIZED;
Expand Down
6 changes: 3 additions & 3 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ static inline int
_PyObject_HasDeferredRefcount(PyObject *op)
{
#ifdef Py_GIL_DISABLED
return (op->ob_gc_bits & _PyGC_BITS_DEFERRED) != 0;
return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_DEFERRED);
#else
return 0;
#endif
Expand Down Expand Up @@ -320,7 +320,7 @@ static inline void _PyObject_GC_TRACK(
"object already tracked by the garbage collector",
filename, lineno, __func__);
#ifdef Py_GIL_DISABLED
op->ob_gc_bits |= _PyGC_BITS_TRACKED;
_PyObject_SET_GC_BITS(op, _PyGC_BITS_TRACKED);
#else
PyGC_Head *gc = _Py_AS_GC(op);
_PyObject_ASSERT_FROM(op,
Expand Down Expand Up @@ -361,7 +361,7 @@ static inline void _PyObject_GC_UNTRACK(
filename, lineno, __func__);

#ifdef Py_GIL_DISABLED
op->ob_gc_bits &= ~_PyGC_BITS_TRACKED;
_PyObject_CLEAR_GC_BITS(op, _PyGC_BITS_TRACKED);
#else
PyGC_Head *gc = _Py_AS_GC(op);
PyGC_Head *prev = _PyGCHead_PREV(gc);
Expand Down
2 changes: 1 addition & 1 deletion Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -2430,7 +2430,7 @@ _PyObject_SetDeferredRefcount(PyObject *op)
assert(PyType_IS_GC(Py_TYPE(op)));
assert(_Py_IsOwnedByCurrentThread(op));
assert(op->ob_ref_shared == 0);
op->ob_gc_bits |= _PyGC_BITS_DEFERRED;
_PyObject_SET_GC_BITS(op, _PyGC_BITS_DEFERRED);
op->ob_ref_local += 1;
op->ob_ref_shared = _Py_REF_QUEUED;
#endif
Expand Down
3 changes: 0 additions & 3 deletions Tools/tsan/suppressions_free_threading.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ race:_PyImport_AcquireLock
race:_PyImport_ReleaseLock
race:_PyInterpreterState_SetNotRunningMain
race:_PyInterpreterState_IsRunningMain
race:_PyObject_GC_IS_SHARED
race:_PyObject_GC_SET_SHARED
race:_PyObject_GC_TRACK
race:_PyType_HasFeature
race:assign_version_tag
race:compare_unicode_unicode
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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