@@ -160,6 +160,132 @@ def task():
160
160
f"Exception ignored in thread started by { task !r} " )
161
161
self .assertIsNotNone (cm .unraisable .exc_traceback )
162
162
163
+ def test_join_thread (self ):
164
+ finished = []
165
+
166
+ def task ():
167
+ time .sleep (0.05 )
168
+ finished .append (thread .get_ident ())
169
+
170
+ with threading_helper .wait_threads_exit ():
171
+ handle = thread .start_joinable_thread (task )
172
+ handle .join ()
173
+ self .assertEqual (len (finished ), 1 )
174
+ self .assertEqual (handle .ident , finished [0 ])
175
+
176
+ def test_join_thread_already_exited (self ):
177
+ def task ():
178
+ pass
179
+
180
+ with threading_helper .wait_threads_exit ():
181
+ handle = thread .start_joinable_thread (task )
182
+ time .sleep (0.05 )
183
+ handle .join ()
184
+
185
+ def test_join_several_times (self ):
186
+ def task ():
187
+ pass
188
+
189
+ with threading_helper .wait_threads_exit ():
190
+ handle = thread .start_joinable_thread (task )
191
+ handle .join ()
192
+ with self .assertRaisesRegex (ValueError , "not joinable" ):
193
+ handle .join ()
194
+
195
+ def test_joinable_not_joined (self ):
196
+ handle_destroyed = thread .allocate_lock ()
197
+ handle_destroyed .acquire ()
198
+
199
+ def task ():
200
+ handle_destroyed .acquire ()
201
+
202
+ with threading_helper .wait_threads_exit ():
203
+ handle = thread .start_joinable_thread (task )
204
+ del handle
205
+ handle_destroyed .release ()
206
+
207
+ def test_join_from_self (self ):
208
+ errors = []
209
+ handles = []
210
+ start_joinable_thread_returned = thread .allocate_lock ()
211
+ start_joinable_thread_returned .acquire ()
212
+ task_tried_to_join = thread .allocate_lock ()
213
+ task_tried_to_join .acquire ()
214
+
215
+ def task ():
216
+ start_joinable_thread_returned .acquire ()
217
+ try :
218
+ handles [0 ].join ()
219
+ except Exception as e :
220
+ errors .append (e )
221
+ finally :
222
+ task_tried_to_join .release ()
223
+
224
+ with threading_helper .wait_threads_exit ():
225
+ handle = thread .start_joinable_thread (task )
226
+ handles .append (handle )
227
+ start_joinable_thread_returned .release ()
228
+ # Can still join after joining failed in other thread
229
+ task_tried_to_join .acquire ()
230
+ handle .join ()
231
+
232
+ assert len (errors ) == 1
233
+ with self .assertRaisesRegex (RuntimeError , "Cannot join current thread" ):
234
+ raise errors [0 ]
235
+
236
+ def test_detach_from_self (self ):
237
+ errors = []
238
+ handles = []
239
+ start_joinable_thread_returned = thread .allocate_lock ()
240
+ start_joinable_thread_returned .acquire ()
241
+ thread_detached = thread .allocate_lock ()
242
+ thread_detached .acquire ()
243
+
244
+ def task ():
245
+ start_joinable_thread_returned .acquire ()
246
+ try :
247
+ handles [0 ].detach ()
248
+ except Exception as e :
249
+ errors .append (e )
250
+ finally :
251
+ thread_detached .release ()
252
+
253
+ with threading_helper .wait_threads_exit ():
254
+ handle = thread .start_joinable_thread (task )
255
+ handles .append (handle )
256
+ start_joinable_thread_returned .release ()
257
+ thread_detached .acquire ()
258
+ with self .assertRaisesRegex (ValueError , "not joinable" ):
259
+ handle .join ()
260
+
261
+ assert len (errors ) == 0
262
+
263
+ def test_detach_then_join (self ):
264
+ lock = thread .allocate_lock ()
265
+ lock .acquire ()
266
+
267
+ def task ():
268
+ lock .acquire ()
269
+
270
+ with threading_helper .wait_threads_exit ():
271
+ handle = thread .start_joinable_thread (task )
272
+ # detach() returns even though the thread is blocked on lock
273
+ handle .detach ()
274
+ # join() then cannot be called anymore
275
+ with self .assertRaisesRegex (ValueError , "not joinable" ):
276
+ handle .join ()
277
+ lock .release ()
278
+
279
+ def test_join_then_detach (self ):
280
+ def task ():
281
+ pass
282
+
283
+ with threading_helper .wait_threads_exit ():
284
+ handle = thread .start_joinable_thread (task )
285
+ handle .join ()
286
+ with self .assertRaisesRegex (ValueError , "not joinable" ):
287
+ handle .detach ()
288
+
163
289
164
290
class Barrier :
165
291
def __init__ (self , num_threads ):
0 commit comments