99#endif
1010
1111#include "pycore_call.h" // _PyObject_CallNoArgs()
12+ #include "pycore_lock.h" // _PyOnceFlag_CallOnce()
1213#include "pycore_runtime.h" // _Py_ID()
1314
1415#include <ffi.h>
@@ -412,6 +413,27 @@ static CThunkObject* CThunkObject_new(ctypes_state *st, Py_ssize_t nargs)
412413 return p ;
413414}
414415
416+ static _PyOnceFlag closure_alloc_init_once = {0 };
417+
418+ // This is done lazily with a once flag rather than in the module exec function,
419+ // because allocating a closure can have some side effects that affect the whole
420+ // process (gh-135893).
421+ static int
422+ closure_alloc_init (void * Py_UNUSED (arg ))
423+ {
424+ // See https://github.com/python/cpython/issues/128485
425+ // This allocates some memory and then frees it to ensure that libffi's
426+ // dlmalloc allocator initializes itself exactly once, avoiding data races
427+ // between threads that don't share a GIL.
428+ void * codeloc = NULL ;
429+ void * ptr = Py_ffi_closure_alloc (sizeof (void * ), & codeloc );
430+ if (ptr == NULL ) {
431+ return -1 ;
432+ }
433+ Py_ffi_closure_free (ptr );
434+ return 0 ;
435+ }
436+
415437CThunkObject * _ctypes_alloc_callback (ctypes_state * st ,
416438 PyObject * callable ,
417439 PyObject * converters ,
@@ -431,6 +453,12 @@ CThunkObject *_ctypes_alloc_callback(ctypes_state *st,
431453
432454 assert (CThunk_CheckExact (st , (PyObject * )p ));
433455
456+ if (_PyOnceFlag_CallOnce (& closure_alloc_init_once ,
457+ closure_alloc_init , NULL ) < 0 ) {
458+ PyErr_NoMemory ();
459+ goto error ;
460+ }
461+
434462 p -> pcl_write = Py_ffi_closure_alloc (sizeof (ffi_closure ), & p -> pcl_exec );
435463 if (p -> pcl_write == NULL ) {
436464 PyErr_NoMemory ();
0 commit comments