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

gh-117657: Make Py_TYPE and Py_SET_TYPE thread safe #120165

Merged
merged 9 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
5 changes: 4 additions & 1 deletion Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,10 @@ PyAPI_FUNC(PyStatus) _PyInterpreterState_New(
#define RARE_EVENT_INTERP_INC(interp, name) \
do { \
/* saturating add */ \
if (interp->rare_events.name < UINT8_MAX) interp->rare_events.name++; \
int val = FT_ATOMIC_LOAD_UINT8_RELAXED(interp->rare_events.name); \
if (val < UINT8_MAX) { \
FT_ATOMIC_STORE_UINT8_RELAXED(interp->rare_events.name, val+1); \
} \
Fidget-Spinner marked this conversation as resolved.
Show resolved Hide resolved
RARE_EVENT_STAT_INC(name); \
} while (0); \

Expand Down
8 changes: 8 additions & 0 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,11 @@ _Py_IsOwnedByCurrentThread(PyObject *ob)

// bpo-39573: The Py_SET_TYPE() function must be used to set an object type.
static inline PyTypeObject* Py_TYPE(PyObject *ob) {
#ifdef Py_GIL_DISABLED
return (PyTypeObject *)_Py_atomic_load_ptr_relaxed(&ob->ob_type);
#else
return ob->ob_type;
#endif
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_TYPE(ob) Py_TYPE(_PyObject_CAST(ob))
Expand Down Expand Up @@ -274,7 +278,11 @@ static inline int Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {


static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) {
#ifdef Py_GIL_DISABLED
_Py_atomic_store_ptr(&ob->ob_type, type);
#else
ob->ob_type = type;
#endif
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
# define Py_SET_TYPE(ob, type) Py_SET_TYPE(_PyObject_CAST(ob), type)
Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_free_threading/test_type.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import threading
import unittest

from concurrent.futures import ThreadPoolExecutor
Expand Down Expand Up @@ -95,6 +96,32 @@ def reader_func():

self.run_one(writer_func, reader_func)

def test___class___modification(self):
class Foo:
pass

class Bar:
pass

thing = Foo()
def work():
foo = thing
for _ in range(10000):
foo.__class__ = Bar
type(foo)
foo.__class__ = Foo
type(foo)


threads = []
for i in range(NTHREADS):
thread = threading.Thread(target=work)
thread.start()
threads.append(thread)

for thread in threads:
thread.join()

def run_one(self, writer_func, reader_func):
writer = Thread(target=writer_func)
readers = []
Expand Down
2 changes: 0 additions & 2 deletions Tools/tsan/suppressions_free_threading.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ race_top:set_contains_key
# https://gist.github.com/colesbury/d13d033f413b4ad07929d044bed86c35
race_top:set_discard_entry
race_top:set_inheritable
race_top:Py_SET_TYPE
race_top:_PyDict_CheckConsistency
race_top:_PyImport_AcquireLock
race_top:_Py_dict_lookup_threadsafe
Expand All @@ -62,7 +61,6 @@ race_top:_PyFrame_Initialize
race_top:PyInterpreterState_ThreadHead
race_top:_PyObject_TryGetInstanceAttribute
race_top:PyThreadState_Next
race_top:Py_TYPE
race_top:PyUnstable_InterpreterFrame_GetLine
race_top:sock_close
race_top:tstate_delete_common
Expand Down
Loading