@@ -384,44 +384,61 @@ slp_eval_frame(PyFrameObject *f)
384384}
385385
386386static void
387- kill_pending_current_main_and_watchdogs (PyThreadState * ts )
387+ get_current_main_and_watchdogs (PyThreadState * ts , PyObject * list )
388388{
389389 PyTaskletObject * t ;
390390
391391 assert (ts != PyThreadState_GET ()); /* don't kill ourself */
392+ assert (PyList_CheckExact (list ));
392393
393394 /* kill watchdogs */
394395 if (ts -> st .watchdogs && PyList_CheckExact (ts -> st .watchdogs )) {
395396 Py_ssize_t i ;
396397 /* we don't kill the "intterupt" slot, number 0 */
397398 for (i = PyList_GET_SIZE (ts -> st .watchdogs ) - 1 ; i > 0 ; i -- ) {
398- PyObject * item = PyList_GET_ITEM (ts -> st .watchdogs , i );
399+ PyObject * item = PyList_GET_ITEM (ts -> st .watchdogs , i );
399400 assert (item && PyTasklet_Check (item ));
400- t = (PyTaskletObject * ) item ;
401- Py_INCREF (t ); /* it is a borrowed ref */
402- PyTasklet_KillEx (t , 1 );
401+ Py_INCREF (item ); /* it is a borrowed ref */
402+ PyList_Append (list , item );
403403 PyErr_Clear ();
404- Py_DECREF (t );
404+ Py_DECREF (item );
405405 }
406406 }
407407 /* kill main */
408408 t = ts -> st .main ;
409409 if (t != NULL ) {
410410 Py_INCREF (t ); /* it is a borrowed ref */
411- PyTasklet_KillEx ( t , 1 );
411+ PyList_Append ( list , ( PyObject * ) t );
412412 PyErr_Clear ();
413413 Py_DECREF (t );
414414 }
415415 /* kill current */
416416 t = ts -> st .current ;
417417 if (t != NULL ) {
418418 Py_INCREF (t ); /* it is a borrowed ref */
419- PyTasklet_KillEx ( t , 1 );
419+ PyList_Append ( list , ( PyObject * ) t );
420420 PyErr_Clear ();
421421 Py_DECREF (t );
422422 }
423423}
424424
425+ static void
426+ kill_pending (PyObject * list )
427+ {
428+ Py_ssize_t i , len ;
429+
430+ assert (list && PyList_CheckExact (list ));
431+
432+ len = PyList_GET_SIZE (list );
433+ for (i = 0 ; i < len ; i ++ ) {
434+ PyTaskletObject * t = (PyTaskletObject * ) PyList_GET_ITEM (list , i );
435+ assert (PyTasklet_Check (t ));
436+ PyTasklet_KillEx (t , 1 );
437+ PyErr_Clear ();
438+ assert (len == PyList_GET_SIZE (list ));
439+ }
440+ }
441+
425442static void
426443run_other_threads (PyObject * * sleep , Py_ssize_t count )
427444{
@@ -584,7 +601,7 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
584601other_threads :
585602 /* Step II of III
586603 *
587- * A separate simple loop to kill tasklets on foreign threads.
604+ * Kill tasklets on foreign threads: .
588605 * Since foreign tasklets are scheduled in their own good time,
589606 * there is no guarantee that they are actually dead when we
590607 * exit this function. Therefore, we also can't clear their thread
@@ -595,20 +612,57 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
595612 PyTaskletObject * t ;
596613 PyObject * sleepfunc = NULL ;
597614 Py_ssize_t count ;
615+ PyObject * tasklet_list = PyList_New (0 );
616+ if (tasklet_list == NULL ) {
617+ PyErr_Clear ();
618+ }
598619
599- /* other threads, first pass: kill (pending) current, main and watchdog tasklets */
600- if (target_ts == NULL ) {
620+ /* Other threads, first pass: kill (pending) current, main and watchdog tasklets
621+ * Iterating over the threads requires the HEAD lock. In order to prevent dead locks,
622+ * we try to avoid interpreter recursions (= no Py_DECREF), while we hold the lock.
623+ */
624+ if (target_ts == NULL && tasklet_list ) {
601625 PyThreadState * ts ;
626+ PyObject * threadid_list = NULL ;
602627 count = 0 ;
628+
629+ /* build a list of tasklets to be killed */
630+ SLP_HEAD_LOCK ();
603631 for (ts = cts -> interp -> tstate_head ; ts != NULL ; ts = ts -> next ) {
604632 if (ts != cts ) {
605633 /* Inactivate thread ts. In case the thread is active,
606634 * it will be killed. If the thread is sleping, it
607635 * continues to sleep.
608636 */
609637 count ++ ;
610- kill_pending_current_main_and_watchdogs (ts );
638+ get_current_main_and_watchdogs (ts , tasklet_list );
639+ }
640+ }
641+ SLP_HEAD_UNLOCK ();
642+
643+ /* get the list of thread ids */
644+ if (PyExc_TaskletExit )
645+ threadid_list = slp_getthreads (Py_None ); /* requires the HEAD lock */
646+
647+ /* kill the tasklets */
648+ kill_pending (tasklet_list );
649+ /* kill the threads */
650+ if (threadid_list != NULL ) {
651+ Py_ssize_t i , len ;
652+ assert (PyList_CheckExact (threadid_list ));
653+ len = PyList_GET_SIZE (threadid_list );
654+ for (i = 0 ; i < len ; i ++ ) {
655+ long thread_id ;
656+ PyObject * item = PyList_GET_ITEM (threadid_list , i );
657+ assert (PyLong_CheckExact (item ));
658+ thread_id = PyLong_AsLong (item );
659+ if (thread_id != cts -> thread_id ) {
660+ /* requires the HEAD lock */
661+ PyThreadState_SetAsyncExc (thread_id , PyExc_TaskletExit );
662+ PyErr_Clear ();
663+ }
611664 }
665+ Py_DECREF (threadid_list );
612666 }
613667 /* We must not release the GIL while we might hold the HEAD-lock.
614668 * Otherwise another thread (usually the thread of the killed tasklet)
@@ -619,10 +673,12 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
619673 /* The other threads might have modified the thread state chain, but fortunately we
620674 * are done with it.
621675 */
622- } else if (target_ts != cts ) {
623- kill_pending_current_main_and_watchdogs (target_ts );
676+ } else if (target_ts != cts && tasklet_list ) {
677+ get_current_main_and_watchdogs (target_ts , tasklet_list );
678+ kill_pending (tasklet_list );
624679 /* Here it is not safe to release the GIL. */
625680 }
681+ Py_XDECREF (tasklet_list );
626682
627683 /* other threads, second pass: kill tasklets with nesting-level > 0 and
628684 * clear tstate if target_ts != NULL && target_ts != cts. */
0 commit comments