@@ -116,62 +116,58 @@ def runner():
116116 support .gc_collect ()
117117 self .assertIsNone (wr ())
118118
119- def test_lingering_task_different_thread_finalized (self ) -> None :
120- tls = threading .local ()
119+ def test_unregister_lingering_tasks_of_exited_thread (self ) -> None :
120+ # gh-159449: a new thread can get the same OS thread id as
121+ # an exited thread whose tasks linger on the interpreter's list.
122+ class InertLoop :
123+ def get_debug (self ):
124+ return False
121125
122- async def never_finishes ():
123- await asyncio .Future ()
126+ def is_running (self ):
127+ return False
128+
129+ def call_soon (self , * args , ** kwargs ):
130+ pass
131+
132+ loop = InertLoop ()
133+ done = threading .Event ()
134+
135+ async def coro ():
136+ pass
124137
125- def make_pending_tasks (loop , count ):
138+ def worker (handoff ):
139+ while handoff :
140+ for task in handoff .pop ():
141+ # complete the task right away, unlike Task.cancel()
142+ asyncio .Future .cancel (task )
126143 tasks = []
127- for _ in range (count ):
128- task = loop .create_task (never_finishes ())
144+ for _ in range (32 ):
145+ c = coro ()
146+ task = self .factory (loop , c )
147+ c .close ()
129148 task ._log_destroy_pending = False
130149 tasks .append (task )
131- return tasks
132-
133- def worker (batches ):
134- loop = asyncio .new_event_loop ()
135- loop .set_task_factory (self .factory )
136- tls_tasks = make_pending_tasks (loop , 8 )
137- for task in tls_tasks :
138- task .get_coro ().close ()
139- tls .tasks = tls_tasks
140- batches .append ((loop , make_pending_tasks (loop , 8 )))
150+ handoff .append (tasks )
141151
142- stop = threading .Event ()
152+ def spawner ():
153+ handoff = []
154+ for _ in range (100 ):
155+ thread = Thread (target = worker , args = (handoff ,))
156+ thread .start ()
157+ thread .join ()
143158
144159 def inspector ():
145- loop = asyncio .new_event_loop ()
146- try :
147- while not stop .is_set ():
148- asyncio .all_tasks (loop )
149- finally :
150- loop .close ()
160+ while not done .is_set ():
161+ asyncio .all_tasks (loop )
151162
152163 inspectors = [Thread (target = inspector ) for _ in range (2 )]
153164 with threading_helper .start_threads (inspectors ):
154165 try :
155- for _ in range (15 ):
156- batches = []
157- workers = [Thread (target = worker , args = (batches ,)) for _ in range (4 )]
158- with threading_helper .start_threads (workers ):
159- pass
160-
161- def cleanup (batch ):
162- loop , tasks = batch
163- for task in tasks :
164- task .cancel ()
165- task .get_coro ().close ()
166- tasks .clear ()
167- loop .close ()
168-
169- cleaners = [Thread (target = cleanup , args = (b ,)) for b in batches ]
170- with threading_helper .start_threads (cleaners ):
171- pass
172- support .gc_collect ()
166+ spawners = [Thread (target = spawner ) for _ in range (4 )]
167+ with threading_helper .start_threads (spawners ):
168+ pass
173169 finally :
174- stop .set ()
170+ done .set ()
175171
176172 def test_run_coroutine_threadsafe (self ) -> None :
177173 results = []
0 commit comments