@@ -3435,6 +3435,97 @@ code_offset_to_line(PyObject* self, PyObject* const* args, Py_ssize_t nargsf)
34353435 return PyLong_FromInt32 (PyCode_Addr2Line (code , offset ));
34363436}
34373437
3438+
3439+ typedef struct {
3440+ PyThread_type_lock lock ;
3441+ int completed ;
3442+ } tracemalloc_track_race_data ;
3443+
3444+ static void
3445+ tracemalloc_track_race_thread (void * raw_data )
3446+ {
3447+ tracemalloc_track_race_data * data = (tracemalloc_track_race_data * )raw_data ;
3448+
3449+ PyTraceMalloc_Track (123 , 10 , 1 );
3450+
3451+ PyThread_acquire_lock (data -> lock , 1 );
3452+ data -> completed += 1 ;
3453+ PyThread_release_lock (data -> lock );
3454+ }
3455+
3456+ // gh-128679: Test fix for tracemalloc.stop() race condition
3457+ static PyObject *
3458+ tracemalloc_track_race (PyObject * self , PyObject * args )
3459+ {
3460+ #define NTHREAD 50
3461+ PyObject * stop = NULL ;
3462+ tracemalloc_track_race_data data = {0 };
3463+
3464+ PyObject * tracemalloc = PyImport_ImportModule ("tracemalloc" );
3465+ if (tracemalloc == NULL ) {
3466+ goto error ;
3467+ }
3468+
3469+ PyObject * start = PyObject_GetAttrString (tracemalloc , "start" );
3470+ if (start == NULL ) {
3471+ goto error ;
3472+ }
3473+ PyObject * res = PyObject_CallFunction (start , "i" , 1 );
3474+ Py_DECREF (start );
3475+ if (res == NULL ) {
3476+ goto error ;
3477+ }
3478+
3479+ stop = PyObject_GetAttrString (tracemalloc , "stop" );
3480+ Py_DECREF (tracemalloc );
3481+ if (stop == NULL ) {
3482+ goto error ;
3483+ }
3484+
3485+ data .lock = PyThread_allocate_lock ();
3486+ if (!data .lock ) {
3487+ PyErr_NoMemory ();
3488+ goto error ;
3489+ }
3490+
3491+ for (size_t i = 0 ; i < NTHREAD ; i ++ ) {
3492+ unsigned long thread = PyThread_start_new_thread (
3493+ tracemalloc_track_race_thread , & data );
3494+ if (thread == (unsigned long )-1 ) {
3495+ PyErr_SetString (PyExc_RuntimeError , "can't start new thread" );
3496+ goto error ;
3497+ }
3498+ }
3499+
3500+ res = PyObject_CallNoArgs (stop );
3501+ Py_CLEAR (stop );
3502+ if (res == NULL ) {
3503+ goto error ;
3504+ }
3505+
3506+ Py_BEGIN_ALLOW_THREADS
3507+ while (1 ) {
3508+ PyThread_acquire_lock (data .lock , 1 );
3509+ int completed = data .completed ;
3510+ PyThread_release_lock (data .lock );
3511+ if (completed >= NTHREAD ) {
3512+ break ;
3513+ }
3514+ sleep (1 );
3515+ }
3516+ Py_END_ALLOW_THREADS
3517+
3518+ Py_RETURN_NONE ;
3519+
3520+ error :
3521+ Py_CLEAR (stop );
3522+ if (data .lock ) {
3523+ PyThread_free_lock (data .lock );
3524+ }
3525+ return NULL ;
3526+ #undef NTHREAD
3527+ }
3528+
34383529static PyMethodDef TestMethods [] = {
34393530 {"set_errno" , set_errno , METH_VARARGS },
34403531 {"test_config" , test_config , METH_NOARGS },
@@ -3578,6 +3669,7 @@ static PyMethodDef TestMethods[] = {
35783669 {"type_freeze" , type_freeze , METH_VARARGS },
35793670 {"test_atexit" , test_atexit , METH_NOARGS },
35803671 {"code_offset_to_line" , _PyCFunction_CAST (code_offset_to_line ), METH_FASTCALL },
3672+ {"tracemalloc_track_race" , tracemalloc_track_race , METH_NOARGS },
35813673 {NULL , NULL } /* sentinel */
35823674};
35833675
0 commit comments