@@ -3322,6 +3322,104 @@ test_atexit(PyObject *self, PyObject *Py_UNUSED(args))
3322
3322
Py_RETURN_NONE ;
3323
3323
}
3324
3324
3325
+
3326
+ static void
3327
+ tracemalloc_track_race_thread (void * data )
3328
+ {
3329
+ PyTraceMalloc_Track (123 , 10 , 1 );
3330
+
3331
+ PyThread_type_lock lock = (PyThread_type_lock )data ;
3332
+ PyThread_release_lock (lock );
3333
+ }
3334
+
3335
+ // gh-128679: Test fix for tracemalloc.stop() race condition
3336
+ static PyObject *
3337
+ tracemalloc_track_race (PyObject * self , PyObject * args )
3338
+ {
3339
+ #define NTHREAD 50
3340
+ PyObject * tracemalloc = NULL ;
3341
+ PyObject * stop = NULL ;
3342
+ PyThread_type_lock locks [NTHREAD ];
3343
+ memset (locks , 0 , sizeof (locks ));
3344
+
3345
+ // Call tracemalloc.start()
3346
+ tracemalloc = PyImport_ImportModule ("tracemalloc" );
3347
+ if (tracemalloc == NULL ) {
3348
+ goto error ;
3349
+ }
3350
+ PyObject * start = PyObject_GetAttrString (tracemalloc , "start" );
3351
+ if (start == NULL ) {
3352
+ goto error ;
3353
+ }
3354
+ PyObject * res = PyObject_CallNoArgs (start );
3355
+ Py_DECREF (start );
3356
+ if (res == NULL ) {
3357
+ goto error ;
3358
+ }
3359
+ Py_DECREF (res );
3360
+
3361
+ stop = PyObject_GetAttrString (tracemalloc , "stop" );
3362
+ Py_CLEAR (tracemalloc );
3363
+ if (stop == NULL ) {
3364
+ goto error ;
3365
+ }
3366
+
3367
+ // Start threads
3368
+ for (size_t i = 0 ; i < NTHREAD ; i ++ ) {
3369
+ PyThread_type_lock lock = PyThread_allocate_lock ();
3370
+ if (!lock ) {
3371
+ PyErr_NoMemory ();
3372
+ goto error ;
3373
+ }
3374
+ locks [i ] = lock ;
3375
+ PyThread_acquire_lock (lock , 1 );
3376
+
3377
+ unsigned long thread ;
3378
+ thread = PyThread_start_new_thread (tracemalloc_track_race_thread ,
3379
+ (void * )lock );
3380
+ if (thread == (unsigned long )-1 ) {
3381
+ PyErr_SetString (PyExc_RuntimeError , "can't start new thread" );
3382
+ goto error ;
3383
+ }
3384
+ }
3385
+
3386
+ // Call tracemalloc.stop() while threads are running
3387
+ res = PyObject_CallNoArgs (stop );
3388
+ Py_CLEAR (stop );
3389
+ if (res == NULL ) {
3390
+ goto error ;
3391
+ }
3392
+ Py_DECREF (res );
3393
+
3394
+ // Wait until threads complete with the GIL released
3395
+ Py_BEGIN_ALLOW_THREADS
3396
+ for (size_t i = 0 ; i < NTHREAD ; i ++ ) {
3397
+ PyThread_type_lock lock = locks [i ];
3398
+ PyThread_acquire_lock (lock , 1 );
3399
+ PyThread_release_lock (lock );
3400
+ }
3401
+ Py_END_ALLOW_THREADS
3402
+
3403
+ // Free threads locks
3404
+ for (size_t i = 0 ; i < NTHREAD ; i ++ ) {
3405
+ PyThread_type_lock lock = locks [i ];
3406
+ PyThread_free_lock (lock );
3407
+ }
3408
+ Py_RETURN_NONE ;
3409
+
3410
+ error :
3411
+ Py_CLEAR (tracemalloc );
3412
+ Py_CLEAR (stop );
3413
+ for (size_t i = 0 ; i < NTHREAD ; i ++ ) {
3414
+ PyThread_type_lock lock = locks [i ];
3415
+ if (lock ) {
3416
+ PyThread_free_lock (lock );
3417
+ }
3418
+ }
3419
+ return NULL ;
3420
+ #undef NTHREAD
3421
+ }
3422
+
3325
3423
static PyMethodDef TestMethods [] = {
3326
3424
{"set_errno" , set_errno , METH_VARARGS },
3327
3425
{"test_config" , test_config , METH_NOARGS },
@@ -3464,6 +3562,7 @@ static PyMethodDef TestMethods[] = {
3464
3562
{"function_set_warning" , function_set_warning , METH_NOARGS },
3465
3563
{"test_critical_sections" , test_critical_sections , METH_NOARGS },
3466
3564
{"test_atexit" , test_atexit , METH_NOARGS },
3565
+ {"tracemalloc_track_race" , tracemalloc_track_race , METH_NOARGS },
3467
3566
{NULL , NULL } /* sentinel */
3468
3567
};
3469
3568
0 commit comments