@@ -103,7 +103,8 @@ typedef enum {
103103 THREAD_HANDLE_NOT_STARTED = 1 ,
104104 THREAD_HANDLE_STARTING = 2 ,
105105 THREAD_HANDLE_RUNNING = 3 ,
106- THREAD_HANDLE_DONE = 4 ,
106+ THREAD_HANDLE_FAILED = 4 ,
107+ THREAD_HANDLE_DONE = 5 ,
107108} ThreadHandleState ;
108109
109110// A handle to wait for thread completion.
@@ -139,6 +140,7 @@ typedef struct {
139140
140141 PyMutex mutex ;
141142
143+ PyEvent thread_is_bootstraped ;
142144 // Set immediately before `thread_run` returns to indicate that the OS
143145 // thread is about to exit. This is used to avoid false positives when
144146 // detecting self-join attempts. See the comment in `ThreadHandle_join()`
@@ -231,6 +233,7 @@ ThreadHandle_new(void)
231233 self -> os_handle = 0 ;
232234 self -> has_os_handle = 0 ;
233235 self -> thread_is_exiting = (PyEvent ){0 };
236+ self -> thread_is_bootstraped = (PyEvent ){0 };
234237 self -> mutex = (PyMutex ){_Py_UNLOCKED };
235238 self -> once = (_PyOnceFlag ){0 };
236239 self -> state = THREAD_HANDLE_NOT_STARTED ;
@@ -286,7 +289,8 @@ ThreadHandle_decref(ThreadHandle *self)
286289 // 1. This is the destructor; nothing else holds a reference.
287290 // 2. The refcount going to zero is a "synchronizes-with" event; all
288291 // changes from other threads are visible.
289- if (self -> state == THREAD_HANDLE_RUNNING && !detach_thread (self )) {
292+ if ((self -> state == THREAD_HANDLE_RUNNING || self -> state == THREAD_HANDLE_FAILED )
293+ && !detach_thread (self )) {
290294 self -> state = THREAD_HANDLE_DONE ;
291295 }
292296
@@ -322,6 +326,7 @@ _PyThread_AfterFork(struct _pythread_runtime_state *state)
322326 handle -> once = (_PyOnceFlag ){_Py_ONCE_INITIALIZED };
323327 handle -> mutex = (PyMutex ){_Py_UNLOCKED };
324328 _PyEvent_Notify (& handle -> thread_is_exiting );
329+ _PyEvent_Notify (& handle -> thread_is_bootstraped );
325330 llist_remove (node );
326331 remove_from_shutdown_handles (handle );
327332 }
@@ -393,6 +398,9 @@ thread_run(void *boot_raw)
393398 PyErr_FormatUnraisable (
394399 "Exception ignored in thread started by %R" , boot -> func );
395400 }
401+ // Notify that the bootstraped is done and failed (e.g. Memory error).
402+ set_thread_handle_state (handle , THREAD_HANDLE_FAILED );
403+ _PyEvent_Notify (& handle -> thread_is_bootstraped );
396404 }
397405 else {
398406 Py_DECREF (res );
@@ -502,7 +510,10 @@ static int
502510join_thread (void * arg )
503511{
504512 ThreadHandle * handle = (ThreadHandle * )arg ;
505- assert (get_thread_handle_state (handle ) == THREAD_HANDLE_RUNNING );
513+ assert (
514+ get_thread_handle_state (handle ) == THREAD_HANDLE_RUNNING ||
515+ get_thread_handle_state (handle ) == THREAD_HANDLE_FAILED
516+ );
506517 PyThread_handle_t os_handle ;
507518 if (ThreadHandle_get_os_handle (handle , & os_handle )) {
508519 int err = 0 ;
@@ -707,6 +718,46 @@ PyThreadHandleObject_join(PyObject *op, PyObject *args)
707718 Py_RETURN_NONE ;
708719}
709720
721+ static PyObject *
722+ PyThreadHandleObject_is_bootstraped (PyObject * op , PyObject * Py_UNUSED (dummy ))
723+ {
724+ PyThreadHandleObject * self = PyThreadHandleObject_CAST (op );
725+ if (_PyEvent_IsSet (& self -> handle -> thread_is_bootstraped )) {
726+ Py_RETURN_TRUE ;
727+ }
728+ else {
729+ Py_RETURN_FALSE ;
730+ }
731+ }
732+
733+ static PyObject *
734+ PyThreadHandleObject_wait_bootstraped (PyObject * op , PyObject * Py_UNUSED (dummy ))
735+ {
736+ PyThreadHandleObject * self = PyThreadHandleObject_CAST (op );
737+ PyEvent_Wait (& self -> handle -> thread_is_bootstraped );
738+ Py_RETURN_NONE ;
739+ }
740+
741+ static PyObject *
742+ PyThreadHandleObject_set_bootstraped (PyObject * op , PyObject * Py_UNUSED (dummy ))
743+ {
744+ PyThreadHandleObject * self = PyThreadHandleObject_CAST (op );
745+ _PyEvent_Notify (& self -> handle -> thread_is_bootstraped );
746+ Py_RETURN_NONE ;
747+ }
748+
749+ static PyObject *
750+ PyThreadHandleObject_is_failed (PyObject * op , PyObject * Py_UNUSED (dummy ))
751+ {
752+ PyThreadHandleObject * self = PyThreadHandleObject_CAST (op );
753+ if (get_thread_handle_state (self -> handle ) == THREAD_HANDLE_FAILED ) {
754+ Py_RETURN_TRUE ;
755+ }
756+ else {
757+ Py_RETURN_FALSE ;
758+ }
759+ }
760+
710761static PyObject *
711762PyThreadHandleObject_is_done (PyObject * op , PyObject * Py_UNUSED (dummy ))
712763{
@@ -740,6 +791,10 @@ static PyGetSetDef ThreadHandle_getsetlist[] = {
740791static PyMethodDef ThreadHandle_methods [] = {
741792 {"join" , PyThreadHandleObject_join , METH_VARARGS , NULL },
742793 {"_set_done" , PyThreadHandleObject_set_done , METH_NOARGS , NULL },
794+ {"wait_bootstraped" , PyThreadHandleObject_wait_bootstraped , METH_NOARGS , NULL },
795+ {"set_bootstraped" , PyThreadHandleObject_set_bootstraped , METH_NOARGS , NULL },
796+ {"is_bootstraped" , PyThreadHandleObject_is_bootstraped , METH_NOARGS , NULL },
797+ {"is_failed" , PyThreadHandleObject_is_failed , METH_NOARGS , NULL },
743798 {"is_done" , PyThreadHandleObject_is_done , METH_NOARGS , NULL },
744799 {0 , 0 }
745800};
@@ -2025,8 +2080,8 @@ thread_PyThread_start_joinable_thread(PyObject *module, PyObject *fargs,
20252080 hobj ) < 0 ) {
20262081 return NULL ;
20272082 }
2028-
2029- if (hobj == Py_None ) {
2083+ int no_handle_arg = ( hobj == Py_None );
2084+ if (no_handle_arg ) {
20302085 hobj = (PyObject * )PyThreadHandleObject_new (state -> thread_handle_type );
20312086 if (hobj == NULL ) {
20322087 return NULL ;
@@ -2047,6 +2102,23 @@ thread_PyThread_start_joinable_thread(PyObject *module, PyObject *fargs,
20472102 Py_DECREF (hobj );
20482103 return NULL ;
20492104 }
2105+
2106+ // gh-140746: catch error before thread really start
2107+ PyThreadHandleObject * thread_handle = PyThreadHandleObject_CAST (hobj );
2108+ if (no_handle_arg ) {
2109+ // If the handle is created by this function, we can be sure that
2110+ // the thread is not started before this point. Here, we simulate
2111+ // the thread bootstrap process.
2112+ _PyEvent_Notify (& thread_handle -> handle -> thread_is_bootstraped );
2113+ }
2114+ PyEvent_Wait (& thread_handle -> handle -> thread_is_bootstraped );
2115+
2116+ if (get_thread_handle_state (thread_handle -> handle ) == THREAD_HANDLE_FAILED ) {
2117+ PyErr_SetString (ThreadError , "call to/in _bootstrap/_bootstrap_inner failed" );
2118+ Py_DECREF (hobj );
2119+ return NULL ;
2120+ }
2121+
20502122 return (PyObject * ) hobj ;
20512123}
20522124
@@ -2467,6 +2539,7 @@ thread__make_thread_handle(PyObject *module, PyObject *identobj)
24672539 hobj -> handle -> ident = ident ;
24682540 hobj -> handle -> state = THREAD_HANDLE_RUNNING ;
24692541 PyMutex_Unlock (& hobj -> handle -> mutex );
2542+ _PyEvent_Notify (& hobj -> handle -> thread_is_bootstraped );
24702543 return (PyObject * ) hobj ;
24712544}
24722545
0 commit comments