Skip to content

Commit 07525c9

Browse files
authored
gh-116818: Make sys.settrace, sys.setprofile, and monitoring thread-safe (#116775)
Makes sys.settrace, sys.setprofile, and monitoring generally thread-safe. Mostly uses a stop-the-world approach and synchronization around the code object's _co_instrumentation_version. There may be a little bit of extra synchronization around the monitoring data that's required to be TSAN clean.
1 parent b45af00 commit 07525c9

18 files changed

+528
-61
lines changed

Include/cpython/pyatomic.h

+8
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,16 @@ _Py_atomic_store_ullong_relaxed(unsigned long long *obj,
465465
static inline void *
466466
_Py_atomic_load_ptr_acquire(const void *obj);
467467

468+
static inline uintptr_t
469+
_Py_atomic_load_uintptr_acquire(const uintptr_t *obj);
470+
468471
// Stores `*obj = value` (release operation)
469472
static inline void
470473
_Py_atomic_store_ptr_release(void *obj, void *value);
471474

475+
static inline void
476+
_Py_atomic_store_uintptr_release(uintptr_t *obj, uintptr_t value);
477+
472478
static inline void
473479
_Py_atomic_store_ssize_release(Py_ssize_t *obj, Py_ssize_t value);
474480

@@ -491,6 +497,8 @@ static inline Py_ssize_t
491497
_Py_atomic_load_ssize_acquire(const Py_ssize_t *obj);
492498

493499

500+
501+
494502
// --- _Py_atomic_fence ------------------------------------------------------
495503

496504
// Sequential consistency fence. C11 fences have complex semantics. When

Include/cpython/pyatomic_gcc.h

+8
Original file line numberDiff line numberDiff line change
@@ -492,10 +492,18 @@ static inline void *
492492
_Py_atomic_load_ptr_acquire(const void *obj)
493493
{ return (void *)__atomic_load_n((void **)obj, __ATOMIC_ACQUIRE); }
494494

495+
static inline uintptr_t
496+
_Py_atomic_load_uintptr_acquire(const uintptr_t *obj)
497+
{ return (uintptr_t)__atomic_load_n((uintptr_t *)obj, __ATOMIC_ACQUIRE); }
498+
495499
static inline void
496500
_Py_atomic_store_ptr_release(void *obj, void *value)
497501
{ __atomic_store_n((void **)obj, value, __ATOMIC_RELEASE); }
498502

503+
static inline void
504+
_Py_atomic_store_uintptr_release(uintptr_t *obj, uintptr_t value)
505+
{ __atomic_store_n(obj, value, __ATOMIC_RELEASE); }
506+
499507
static inline void
500508
_Py_atomic_store_int_release(int *obj, int value)
501509
{ __atomic_store_n(obj, value, __ATOMIC_RELEASE); }

Include/cpython/pyatomic_msc.h

+25
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,18 @@ _Py_atomic_load_ptr_acquire(const void *obj)
914914
#endif
915915
}
916916

917+
static inline uintptr_t
918+
_Py_atomic_load_uintptr_acquire(const uintptr_t *obj)
919+
{
920+
#if defined(_M_X64) || defined(_M_IX86)
921+
return *(uintptr_t volatile *)obj;
922+
#elif defined(_M_ARM64)
923+
return (uintptr_t)__ldar64((unsigned __int64 volatile *)obj);
924+
#else
925+
# error "no implementation of _Py_atomic_load_uintptr_acquire"
926+
#endif
927+
}
928+
917929
static inline void
918930
_Py_atomic_store_ptr_release(void *obj, void *value)
919931
{
@@ -926,6 +938,19 @@ _Py_atomic_store_ptr_release(void *obj, void *value)
926938
#endif
927939
}
928940

941+
static inline void
942+
_Py_atomic_store_uintptr_release(uintptr_t *obj, uintptr_t value)
943+
{
944+
#if defined(_M_X64) || defined(_M_IX86)
945+
*(uintptr_t volatile *)obj = value;
946+
#elif defined(_M_ARM64)
947+
_Py_atomic_ASSERT_ARG_TYPE(unsigned __int64);
948+
__stlr64((unsigned __int64 volatile *)obj, (unsigned __int64)value);
949+
#else
950+
# error "no implementation of _Py_atomic_store_uintptr_release"
951+
#endif
952+
}
953+
929954
static inline void
930955
_Py_atomic_store_int_release(int *obj, int value)
931956
{

Include/cpython/pyatomic_std.h

+16
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,14 @@ _Py_atomic_load_ptr_acquire(const void *obj)
863863
memory_order_acquire);
864864
}
865865

866+
static inline uintptr_t
867+
_Py_atomic_load_uintptr_acquire(const uintptr_t *obj)
868+
{
869+
_Py_USING_STD;
870+
return atomic_load_explicit((const _Atomic(uintptr_t)*)obj,
871+
memory_order_acquire);
872+
}
873+
866874
static inline void
867875
_Py_atomic_store_ptr_release(void *obj, void *value)
868876
{
@@ -871,6 +879,14 @@ _Py_atomic_store_ptr_release(void *obj, void *value)
871879
memory_order_release);
872880
}
873881

882+
static inline void
883+
_Py_atomic_store_uintptr_release(uintptr_t *obj, uintptr_t value)
884+
{
885+
_Py_USING_STD;
886+
atomic_store_explicit((_Atomic(uintptr_t)*)obj, value,
887+
memory_order_release);
888+
}
889+
874890
static inline void
875891
_Py_atomic_store_int_release(int *obj, int value)
876892
{

Include/internal/pycore_ceval_state.h

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ struct _ceval_runtime_state {
6363
} perf;
6464
/* Pending calls to be made only on the main thread. */
6565
struct _pending_calls pending_mainthread;
66+
PyMutex sys_trace_profile_mutex;
6667
};
6768

6869
#ifdef PY_HAVE_PERF_TRAMPOLINE

Include/internal/pycore_gc.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ static inline void _PyObject_GC_SET_SHARED(PyObject *op) {
9393
* threads and needs special purpose when freeing due to
9494
* the possibility of in-flight lock-free reads occurring.
9595
* Objects with this bit that are GC objects will automatically
96-
* delay-freed by PyObject_GC_Del. */
96+
* delay-freed by PyObject_GC_Del. */
9797
static inline int _PyObject_GC_IS_SHARED_INLINE(PyObject *op) {
9898
return (op->ob_gc_bits & _PyGC_BITS_SHARED_INLINE) != 0;
9999
}

Include/internal/pycore_pyatomic_ft_wrappers.h

+14
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,34 @@ extern "C" {
2626
_Py_atomic_load_ssize_relaxed(&value)
2727
#define FT_ATOMIC_STORE_PTR(value, new_value) \
2828
_Py_atomic_store_ptr(&value, new_value)
29+
#define FT_ATOMIC_LOAD_PTR_ACQUIRE(value) \
30+
_Py_atomic_load_ptr_acquire(&value)
31+
#define FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(value) \
32+
_Py_atomic_load_uintptr_acquire(&value)
2933
#define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) \
3034
_Py_atomic_store_ptr_relaxed(&value, new_value)
3135
#define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) \
3236
_Py_atomic_store_ptr_release(&value, new_value)
37+
#define FT_ATOMIC_STORE_UINTPTR_RELEASE(value, new_value) \
38+
_Py_atomic_store_uintptr_release(&value, new_value)
3339
#define FT_ATOMIC_STORE_SSIZE_RELAXED(value, new_value) \
3440
_Py_atomic_store_ssize_relaxed(&value, new_value)
41+
#define FT_ATOMIC_STORE_UINT8_RELAXED(value, new_value) \
42+
_Py_atomic_store_uint8_relaxed(&value, new_value)
43+
3544
#else
3645
#define FT_ATOMIC_LOAD_PTR(value) value
3746
#define FT_ATOMIC_LOAD_SSIZE(value) value
3847
#define FT_ATOMIC_LOAD_SSIZE_RELAXED(value) value
3948
#define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value
49+
#define FT_ATOMIC_LOAD_PTR_ACQUIRE(value) value
50+
#define FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(value) value
4051
#define FT_ATOMIC_STORE_PTR_RELAXED(value, new_value) value = new_value
4152
#define FT_ATOMIC_STORE_PTR_RELEASE(value, new_value) value = new_value
53+
#define FT_ATOMIC_STORE_UINTPTR_RELEASE(value, new_value) value = new_value
4254
#define FT_ATOMIC_STORE_SSIZE_RELAXED(value, new_value) value = new_value
55+
#define FT_ATOMIC_STORE_UINT8_RELAXED(value, new_value) value = new_value
56+
4357
#endif
4458

4559
#ifdef __cplusplus
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import os
2+
3+
from test import support
4+
5+
6+
def load_tests(*args):
7+
return support.load_package_tests(os.path.dirname(__file__), *args)

0 commit comments

Comments
 (0)