@@ -1053,21 +1053,21 @@ _localdummy_destroyed(PyObject *localweakref, PyObject *dummyweakref)
10531053/* Module functions */
10541054
10551055struct bootstate {
1056- PyInterpreterState * interp ;
1056+ PyThreadState * tstate ;
10571057 PyObject * func ;
10581058 PyObject * args ;
10591059 PyObject * kwargs ;
1060- PyThreadState * tstate ;
1061- _PyRuntimeState * runtime ;
10621060};
10631061
10641062
10651063static void
1066- thread_bootstate_free (struct bootstate * boot )
1064+ thread_bootstate_free (struct bootstate * boot , int decref )
10671065{
1068- Py_DECREF (boot -> func );
1069- Py_DECREF (boot -> args );
1070- Py_XDECREF (boot -> kwargs );
1066+ if (decref ) {
1067+ Py_DECREF (boot -> func );
1068+ Py_DECREF (boot -> args );
1069+ Py_XDECREF (boot -> kwargs );
1070+ }
10711071 PyMem_Free (boot );
10721072}
10731073
@@ -1078,9 +1078,24 @@ thread_run(void *boot_raw)
10781078 struct bootstate * boot = (struct bootstate * ) boot_raw ;
10791079 PyThreadState * tstate = boot -> tstate ;
10801080
1081- // gh-104690: If Python is being finalized and PyInterpreterState_Delete()
1082- // was called, tstate becomes a dangling pointer.
1083- assert (_PyThreadState_CheckConsistency (tstate ));
1081+ // gh-108987: If _thread.start_new_thread() is called before or while
1082+ // Python is being finalized, thread_run() can called *after*.
1083+ // _PyRuntimeState_SetFinalizing() is called. At this point, all Python
1084+ // threads must exit, except of the thread calling Py_Finalize() whch holds
1085+ // the GIL and must not exit.
1086+ //
1087+ // At this stage, tstate can be a dangling pointer (point to freed memory),
1088+ // it's ok to call _PyThreadState_MustExit() with a dangling pointer.
1089+ if (_PyThreadState_MustExit (tstate )) {
1090+ // Don't call PyThreadState_Clear() nor _PyThreadState_DeleteCurrent().
1091+ // These functions are called on tstate indirectly by Py_Finalize()
1092+ // which calls _PyInterpreterState_Clear().
1093+ //
1094+ // Py_DECREF() cannot be called because the GIL is not held: leak
1095+ // references on purpose. Python is being finalized anyway.
1096+ thread_bootstate_free (boot , 0 );
1097+ goto exit ;
1098+ }
10841099
10851100 tstate -> thread_id = PyThread_get_thread_ident ();
10861101#ifdef PY_HAVE_THREAD_NATIVE_ID
@@ -1105,20 +1120,22 @@ thread_run(void *boot_raw)
11051120 Py_DECREF (res );
11061121 }
11071122
1108- thread_bootstate_free (boot );
1123+ thread_bootstate_free (boot , 1 );
1124+
11091125 tstate -> interp -> threads .count -- ;
11101126 PyThreadState_Clear (tstate );
11111127 _PyThreadState_DeleteCurrent (tstate );
11121128
1129+ exit :
11131130 // bpo-44434: Don't call explicitly PyThread_exit_thread(). On Linux with
11141131 // the glibc, pthread_exit() can abort the whole process if dlopen() fails
11151132 // to open the libgcc_s.so library (ex: EMFILE error).
1133+ return ;
11161134}
11171135
11181136static PyObject *
11191137thread_PyThread_start_new_thread (PyObject * self , PyObject * fargs )
11201138{
1121- _PyRuntimeState * runtime = & _PyRuntime ;
11221139 PyObject * func , * args , * kwargs = NULL ;
11231140
11241141 if (!PyArg_UnpackTuple (fargs , "start_new_thread" , 2 , 3 ,
@@ -1151,13 +1168,11 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
11511168 if (boot == NULL ) {
11521169 return PyErr_NoMemory ();
11531170 }
1154- boot -> interp = _PyInterpreterState_GET ();
1155- boot -> tstate = _PyThreadState_Prealloc (boot -> interp );
1171+ boot -> tstate = _PyThreadState_Prealloc (interp );
11561172 if (boot -> tstate == NULL ) {
11571173 PyMem_Free (boot );
11581174 return PyErr_NoMemory ();
11591175 }
1160- boot -> runtime = runtime ;
11611176 boot -> func = Py_NewRef (func );
11621177 boot -> args = Py_NewRef (args );
11631178 boot -> kwargs = Py_XNewRef (kwargs );
@@ -1166,7 +1181,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
11661181 if (ident == PYTHREAD_INVALID_THREAD_ID ) {
11671182 PyErr_SetString (ThreadError , "can't start new thread" );
11681183 PyThreadState_Clear (boot -> tstate );
1169- thread_bootstate_free (boot );
1184+ thread_bootstate_free (boot , 1 );
11701185 return NULL ;
11711186 }
11721187 return PyLong_FromUnsignedLong (ident );
0 commit comments