GeezerVison is showing github.com — [3.13] gh-117657: Make Py_TYPE and Py_SET_TYPE thread safe (GH-120165… · python/cpython@91c4444
File treeExpand file treeCollapse file tree Open diff view settings Lib/test/test_free_threading
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Include/internal/pycore_interp.h +4-1Lines changed: 4 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -401,7 +401,10 @@ PyAPI_FUNC(PyStatus) _PyInterpreterState_New( | 401 | 401 | #define RARE_EVENT_INTERP_INC(interp, name) \ | 402 | 402 | do { \ | 403 | 403 | /* saturating add */ \ | 404 | | - if (interp->rare_events.name < UINT8_MAX) interp->rare_events.name++; \ | | 404 | + int val = FT_ATOMIC_LOAD_UINT8_RELAXED(interp->rare_events.name); \ | | 405 | + if (val < UINT8_MAX) { \ | | 406 | + FT_ATOMIC_STORE_UINT8(interp->rare_events.name, val + 1); \ | | 407 | + } \ | 405 | 408 | RARE_EVENT_STAT_INC(name); \ | 406 | 409 | } while (0); \ | 407 | 410 |
| |
Collapse file Copy file name to clipboardExpand all lines: Include/object.h +8Lines changed: 8 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -331,7 +331,11 @@ static inline Py_ssize_t Py_REFCNT(PyObject *ob) { | 331 | 331 |
| 332 | 332 | // bpo-39573: The Py_SET_TYPE() function must be used to set an object type. | 333 | 333 | static inline PyTypeObject* Py_TYPE(PyObject *ob) { | | 334 | +#ifdef Py_GIL_DISABLED | | 335 | + return (PyTypeObject *)_Py_atomic_load_ptr_relaxed(&ob->ob_type); | | 336 | +#else | 334 | 337 | return ob->ob_type; | | 338 | +#endif | 335 | 339 | } | 336 | 340 | #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000 | 337 | 341 | # define Py_TYPE(ob) Py_TYPE(_PyObject_CAST(ob)) | @@ -421,7 +425,11 @@ static inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { | 421 | 425 |
| 422 | 426 |
| 423 | 427 | static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { | | 428 | +#ifdef Py_GIL_DISABLED | | 429 | + _Py_atomic_store_ptr(&ob->ob_type, type); | | 430 | +#else | 424 | 431 | ob->ob_type = type; | | 432 | +#endif | 425 | 433 | } | 426 | 434 | #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000 | 427 | 435 | # define Py_SET_TYPE(ob, type) Py_SET_TYPE(_PyObject_CAST(ob), type) | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_free_threading/test_type.py +26Lines changed: 26 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -96,6 +96,32 @@ def reader_func(): | 96 | 96 |
| 97 | 97 | self.run_one(writer_func, reader_func) | 98 | 98 |
| | 99 | + def test___class___modification(self): | | 100 | + class Foo: | | 101 | + pass | | 102 | + | | 103 | + class Bar: | | 104 | + pass | | 105 | + | | 106 | + thing = Foo() | | 107 | + def work(): | | 108 | + foo = thing | | 109 | + for _ in range(10000): | | 110 | + foo.__class__ = Bar | | 111 | + type(foo) | | 112 | + foo.__class__ = Foo | | 113 | + type(foo) | | 114 | + | | 115 | + | | 116 | + threads = [] | | 117 | + for i in range(NTHREADS): | | 118 | + thread = threading.Thread(target=work) | | 119 | + thread.start() | | 120 | + threads.append(thread) | | 121 | + | | 122 | + for thread in threads: | | 123 | + thread.join() | | 124 | + | 99 | 125 | def run_one(self, writer_func, reader_func): | 100 | 126 | writer = Thread(target=writer_func) | 101 | 127 | readers = [] | |
Collapse file Copy file name to clipboardExpand all lines: Objects/typeobject.c +7-1Lines changed: 7 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -6474,9 +6474,15 @@ object_set_class(PyObject *self, PyObject *value, void *closure) | 6474 | 6474 | if (newto->tp_flags & Py_TPFLAGS_HEAPTYPE) { | 6475 | 6475 | Py_INCREF(newto); | 6476 | 6476 | } | | 6477 | + Py_BEGIN_CRITICAL_SECTION(self); | | 6478 | + // The real Py_TYPE(self) (`oldto`) may have changed from | | 6479 | + // underneath us in another thread, so we re-fetch it here. | | 6480 | + oldto = Py_TYPE(self); | 6477 | 6481 | Py_SET_TYPE(self, newto); | 6478 | | - if (oldto->tp_flags & Py_TPFLAGS_HEAPTYPE) | | 6482 | + Py_END_CRITICAL_SECTION(); | | 6483 | + if (oldto->tp_flags & Py_TPFLAGS_HEAPTYPE) { | 6479 | 6484 | Py_DECREF(oldto); | | 6485 | + } | 6480 | 6486 |
| 6481 | 6487 | RARE_EVENT_INC(set_class); | 6482 | 6488 | return 0; | |
Collapse file Copy file name to clipboardExpand all lines: Tools/tsan/suppressions_free_threading.txt -2Lines changed: 0 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -37,7 +37,6 @@ race_top:set_contains_key | 37 | 37 | # https://gist.github.com/colesbury/d13d033f413b4ad07929d044bed86c35 | 38 | 38 | race_top:set_discard_entry | 39 | 39 | race_top:set_inheritable | 40 | | -race_top:Py_SET_TYPE | 41 | 40 | race_top:_PyDict_CheckConsistency | 42 | 41 | race_top:_Py_dict_lookup_threadsafe | 43 | 42 | race_top:_multiprocessing_SemLock_acquire_impl | @@ -58,7 +57,6 @@ race_top:_PyFrame_Initialize | 58 | 57 | race_top:PyInterpreterState_ThreadHead | 59 | 58 | race_top:_PyObject_TryGetInstanceAttribute | 60 | 59 | race_top:PyThreadState_Next | 61 | | -race_top:Py_TYPE | 62 | 60 | race_top:PyUnstable_InterpreterFrame_GetLine | 63 | 61 | race_top:sock_close | 64 | 62 | race_top:tstate_delete_common | |
|