@@ -3238,6 +3238,105 @@ test_atexit(PyObject *self, PyObject *Py_UNUSED(args))
3238
3238
3239
3239
static PyObject * test_buildvalue_issue38913 (PyObject * , PyObject * );
3240
3240
3241
+
3242
+ static void
3243
+ tracemalloc_track_race_thread (void * data )
3244
+ {
3245
+ PyTraceMalloc_Track (123 , 10 , 1 );
3246
+
3247
+ PyThread_type_lock lock = (PyThread_type_lock )data ;
3248
+ PyThread_release_lock (lock );
3249
+ }
3250
+
3251
+ // gh-128679: Test fix for tracemalloc.stop() race condition
3252
+ static PyObject *
3253
+ tracemalloc_track_race (PyObject * self , PyObject * args )
3254
+ {
3255
+ #define NTHREAD 50
3256
+ PyObject * tracemalloc = NULL ;
3257
+ PyObject * stop = NULL ;
3258
+ PyThread_type_lock locks [NTHREAD ];
3259
+ memset (locks , 0 , sizeof (locks ));
3260
+
3261
+ // Call tracemalloc.start()
3262
+ tracemalloc = PyImport_ImportModule ("tracemalloc" );
3263
+ if (tracemalloc == NULL ) {
3264
+ goto error ;
3265
+ }
3266
+ PyObject * start = PyObject_GetAttrString (tracemalloc , "start" );
3267
+ if (start == NULL ) {
3268
+ goto error ;
3269
+ }
3270
+ PyObject * res = PyObject_CallNoArgs (start );
3271
+ Py_DECREF (start );
3272
+ if (res == NULL ) {
3273
+ goto error ;
3274
+ }
3275
+ Py_DECREF (res );
3276
+
3277
+ stop = PyObject_GetAttrString (tracemalloc , "stop" );
3278
+ Py_CLEAR (tracemalloc );
3279
+ if (stop == NULL ) {
3280
+ goto error ;
3281
+ }
3282
+
3283
+ // Start threads
3284
+ for (size_t i = 0 ; i < NTHREAD ; i ++ ) {
3285
+ PyThread_type_lock lock = PyThread_allocate_lock ();
3286
+ if (!lock ) {
3287
+ PyErr_NoMemory ();
3288
+ goto error ;
3289
+ }
3290
+ locks [i ] = lock ;
3291
+ PyThread_acquire_lock (lock , 1 );
3292
+
3293
+ unsigned long thread ;
3294
+ thread = PyThread_start_new_thread (tracemalloc_track_race_thread ,
3295
+ (void * )lock );
3296
+ if (thread == (unsigned long )-1 ) {
3297
+ PyErr_SetString (PyExc_RuntimeError , "can't start new thread" );
3298
+ goto error ;
3299
+ }
3300
+ }
3301
+
3302
+ // Call tracemalloc.stop() while threads are running
3303
+ res = PyObject_CallNoArgs (stop );
3304
+ Py_CLEAR (stop );
3305
+ if (res == NULL ) {
3306
+ goto error ;
3307
+ }
3308
+ Py_DECREF (res );
3309
+
3310
+ // Wait until threads complete with the GIL released
3311
+ Py_BEGIN_ALLOW_THREADS
3312
+ for (size_t i = 0 ; i < NTHREAD ; i ++ ) {
3313
+ PyThread_type_lock lock = locks [i ];
3314
+ PyThread_acquire_lock (lock , 1 );
3315
+ PyThread_release_lock (lock );
3316
+ }
3317
+ Py_END_ALLOW_THREADS
3318
+
3319
+ // Free threads locks
3320
+ for (size_t i = 0 ; i < NTHREAD ; i ++ ) {
3321
+ PyThread_type_lock lock = locks [i ];
3322
+ PyThread_free_lock (lock );
3323
+ }
3324
+ Py_RETURN_NONE ;
3325
+
3326
+ error :
3327
+ Py_CLEAR (tracemalloc );
3328
+ Py_CLEAR (stop );
3329
+ for (size_t i = 0 ; i < NTHREAD ; i ++ ) {
3330
+ PyThread_type_lock lock = locks [i ];
3331
+ if (lock ) {
3332
+ PyThread_free_lock (lock );
3333
+ }
3334
+ }
3335
+ return NULL ;
3336
+ #undef NTHREAD
3337
+ }
3338
+
3339
+
3241
3340
static PyMethodDef TestMethods [] = {
3242
3341
{"set_errno" , set_errno , METH_VARARGS },
3243
3342
{"test_config" , test_config , METH_NOARGS },
@@ -3378,6 +3477,7 @@ static PyMethodDef TestMethods[] = {
3378
3477
{"function_get_kw_defaults" , function_get_kw_defaults , METH_O , NULL },
3379
3478
{"function_set_kw_defaults" , function_set_kw_defaults , METH_VARARGS , NULL },
3380
3479
{"test_atexit" , test_atexit , METH_NOARGS },
3480
+ {"tracemalloc_track_race" , tracemalloc_track_race , METH_NOARGS },
3381
3481
{NULL , NULL } /* sentinel */
3382
3482
};
3383
3483
0 commit comments