@@ -380,44 +380,61 @@ slp_eval_frame(PyFrameObject *f)
380380}
381381
382382static void
383- kill_pending_current_main_and_watchdogs (PyThreadState * ts )
383+ get_current_main_and_watchdogs (PyThreadState * ts , PyObject * list )
384384{
385385 PyTaskletObject * t ;
386386
387387 assert (ts != PyThreadState_GET ()); /* don't kill ourself */
388+ assert (PyList_CheckExact (list ));
388389
389390 /* kill watchdogs */
390391 if (ts -> st .watchdogs && PyList_CheckExact (ts -> st .watchdogs )) {
391392 Py_ssize_t i ;
392393 /* we don't kill the "intterupt" slot, number 0 */
393394 for (i = PyList_GET_SIZE (ts -> st .watchdogs ) - 1 ; i > 0 ; i -- ) {
394- PyObject * item = PyList_GET_ITEM (ts -> st .watchdogs , i );
395+ PyObject * item = PyList_GET_ITEM (ts -> st .watchdogs , i );
395396 assert (item && PyTasklet_Check (item ));
396- t = (PyTaskletObject * ) item ;
397- Py_INCREF (t ); /* it is a borrowed ref */
398- PyTasklet_KillEx (t , 1 );
397+ Py_INCREF (item ); /* it is a borrowed ref */
398+ PyList_Append (list , item );
399399 PyErr_Clear ();
400- Py_DECREF (t );
400+ Py_DECREF (item );
401401 }
402402 }
403403 /* kill main */
404404 t = ts -> st .main ;
405405 if (t != NULL ) {
406406 Py_INCREF (t ); /* it is a borrowed ref */
407- PyTasklet_KillEx ( t , 1 );
407+ PyList_Append ( list , ( PyObject * ) t );
408408 PyErr_Clear ();
409409 Py_DECREF (t );
410410 }
411411 /* kill current */
412412 t = ts -> st .current ;
413413 if (t != NULL ) {
414414 Py_INCREF (t ); /* it is a borrowed ref */
415- PyTasklet_KillEx ( t , 1 );
415+ PyList_Append ( list , ( PyObject * ) t );
416416 PyErr_Clear ();
417417 Py_DECREF (t );
418418 }
419419}
420420
421+ static void
422+ kill_pending (PyObject * list )
423+ {
424+ Py_ssize_t i , len ;
425+
426+ assert (list && PyList_CheckExact (list ));
427+
428+ len = PyList_GET_SIZE (list );
429+ for (i = 0 ; i < len ; i ++ ) {
430+ PyTaskletObject * t = (PyTaskletObject * ) PyList_GET_ITEM (list , i );
431+ assert (PyTasklet_Check (t ));
432+ PyTasklet_KillEx (t , 1 );
433+ PyErr_Clear ();
434+ assert (len == PyList_GET_SIZE (list ));
435+ }
436+ }
437+
421438static void
422439run_other_threads (PyObject * * sleep , Py_ssize_t count )
423440{
@@ -580,7 +597,7 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
580597other_threads :
581598 /* Step II of III
582599 *
583- * A separate simple loop to kill tasklets on foreign threads.
600+ * Kill tasklets on foreign threads: .
584601 * Since foreign tasklets are scheduled in their own good time,
585602 * there is no guarantee that they are actually dead when we
586603 * exit this function. Therefore, we also can't clear their thread
@@ -591,20 +608,57 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
591608 PyTaskletObject * t ;
592609 PyObject * sleepfunc = NULL ;
593610 Py_ssize_t count ;
611+ PyObject * tasklet_list = PyList_New (0 );
612+ if (tasklet_list == NULL ) {
613+ PyErr_Clear ();
614+ }
594615
595- /* other threads, first pass: kill (pending) current, main and watchdog tasklets */
596- if (target_ts == NULL ) {
616+ /* Other threads, first pass: kill (pending) current, main and watchdog tasklets
617+ * Iterating over the threads requires the HEAD lock. In order to prevent dead locks,
618+ * we try to avoid interpreter recursions (= no Py_DECREF), while we hold the lock.
619+ */
620+ if (target_ts == NULL && tasklet_list ) {
597621 PyThreadState * ts ;
622+ PyObject * threadid_list = NULL ;
598623 count = 0 ;
624+
625+ /* build a list of tasklets to be killed */
626+ SLP_HEAD_LOCK ();
599627 for (ts = cts -> interp -> tstate_head ; ts != NULL ; ts = ts -> next ) {
600628 if (ts != cts ) {
601629 /* Inactivate thread ts. In case the thread is active,
602630 * it will be killed. If the thread is sleping, it
603631 * continues to sleep.
604632 */
605633 count ++ ;
606- kill_pending_current_main_and_watchdogs (ts );
634+ get_current_main_and_watchdogs (ts , tasklet_list );
635+ }
636+ }
637+ SLP_HEAD_UNLOCK ();
638+
639+ /* get the list of thread ids */
640+ if (PyExc_TaskletExit )
641+ threadid_list = slp_getthreads (Py_None ); /* requires the HEAD lock */
642+
643+ /* kill the tasklets */
644+ kill_pending (tasklet_list );
645+ /* kill the threads */
646+ if (threadid_list != NULL ) {
647+ Py_ssize_t i , len ;
648+ assert (PyList_CheckExact (threadid_list ));
649+ len = PyList_GET_SIZE (threadid_list );
650+ for (i = 0 ; i < len ; i ++ ) {
651+ long thread_id ;
652+ PyObject * item = PyList_GET_ITEM (threadid_list , i );
653+ assert (PyLong_CheckExact (item ));
654+ thread_id = PyLong_AsLong (item );
655+ if (thread_id != cts -> thread_id ) {
656+ /* requires the HEAD lock */
657+ PyThreadState_SetAsyncExc (thread_id , PyExc_TaskletExit );
658+ PyErr_Clear ();
659+ }
607660 }
661+ Py_DECREF (threadid_list );
608662 }
609663 /* We must not release the GIL while we might hold the HEAD-lock.
610664 * Otherwise another thread (usually the thread of the killed tasklet)
@@ -615,10 +669,12 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
615669 /* The other threads might have modified the thread state chain, but fortunately we
616670 * are done with it.
617671 */
618- } else if (target_ts != cts ) {
619- kill_pending_current_main_and_watchdogs (target_ts );
672+ } else if (target_ts != cts && tasklet_list ) {
673+ get_current_main_and_watchdogs (target_ts , tasklet_list );
674+ kill_pending (tasklet_list );
620675 /* Here it is not safe to release the GIL. */
621676 }
677+ Py_XDECREF (tasklet_list );
622678
623679 /* other threads, second pass: kill tasklets with nesting-level > 0 and
624680 * clear tstate if target_ts != NULL && target_ts != cts. */
0 commit comments