@@ -4303,7 +4303,32 @@ def test_empty(self):
4303
4303
# Mixins
4304
4304
#
4305
4305
4306
- class ProcessesMixin (object ):
4306
+ class BaseMixin (object ):
4307
+ @classmethod
4308
+ def setUpClass (cls ):
4309
+ cls .dangling = (multiprocessing .process ._dangling .copy (),
4310
+ threading ._dangling .copy ())
4311
+
4312
+ @classmethod
4313
+ def tearDownClass (cls ):
4314
+ # bpo-26762: Some multiprocessing objects like Pool create reference
4315
+ # cycles. Trigger a garbage collection to break these cycles.
4316
+ test .support .gc_collect ()
4317
+
4318
+ processes = set (multiprocessing .process ._dangling ) - set (cls .dangling [0 ])
4319
+ if processes :
4320
+ print ('Warning -- Dangling processes: %s' % processes ,
4321
+ file = sys .stderr )
4322
+ processes = None
4323
+
4324
+ threads = set (threading ._dangling ) - set (cls .dangling [1 ])
4325
+ if threads :
4326
+ print ('Warning -- Dangling threads: %s' % threads ,
4327
+ file = sys .stderr )
4328
+ threads = None
4329
+
4330
+
4331
+ class ProcessesMixin (BaseMixin ):
4307
4332
TYPE = 'processes'
4308
4333
Process = multiprocessing .Process
4309
4334
connection = multiprocessing .connection
@@ -4326,7 +4351,7 @@ class ProcessesMixin(object):
4326
4351
RawArray = staticmethod (multiprocessing .RawArray )
4327
4352
4328
4353
4329
- class ManagerMixin (object ):
4354
+ class ManagerMixin (BaseMixin ):
4330
4355
TYPE = 'manager'
4331
4356
Process = multiprocessing .Process
4332
4357
Queue = property (operator .attrgetter ('manager.Queue' ))
@@ -4350,30 +4375,43 @@ def Pool(cls, *args, **kwds):
4350
4375
4351
4376
@classmethod
4352
4377
def setUpClass (cls ):
4378
+ super ().setUpClass ()
4353
4379
cls .manager = multiprocessing .Manager ()
4354
4380
4355
4381
@classmethod
4356
4382
def tearDownClass (cls ):
4357
4383
# only the manager process should be returned by active_children()
4358
4384
# but this can take a bit on slow machines, so wait a few seconds
4359
4385
# if there are other children too (see #17395)
4386
+ start_time = time .monotonic ()
4360
4387
t = 0.01
4361
- while len (multiprocessing .active_children ()) > 1 and t < 5 :
4388
+ while len (multiprocessing .active_children ()) > 1 :
4362
4389
time .sleep (t )
4363
4390
t *= 2
4391
+ dt = time .monotonic () - start_time
4392
+ if dt >= 5.0 :
4393
+ print ("Warning -- multiprocessing.Manager still has %s active "
4394
+ "children after %s seconds"
4395
+ % (multiprocessing .active_children (), dt ),
4396
+ file = sys .stderr )
4397
+ break
4398
+
4364
4399
gc .collect () # do garbage collection
4365
4400
if cls .manager ._number_of_objects () != 0 :
4366
4401
# This is not really an error since some tests do not
4367
4402
# ensure that all processes which hold a reference to a
4368
4403
# managed object have been joined.
4369
- print ('Shared objects which still exist at manager shutdown:' )
4404
+ print ('Warning -- Shared objects which still exist at manager '
4405
+ 'shutdown:' )
4370
4406
print (cls .manager ._debug_info ())
4371
4407
cls .manager .shutdown ()
4372
4408
cls .manager .join ()
4373
4409
cls .manager = None
4374
4410
4411
+ super ().tearDownClass ()
4412
+
4375
4413
4376
- class ThreadsMixin (object ):
4414
+ class ThreadsMixin (BaseMixin ):
4377
4415
TYPE = 'threads'
4378
4416
Process = multiprocessing .dummy .Process
4379
4417
connection = multiprocessing .dummy .connection
@@ -4450,18 +4488,33 @@ def setUpModule():
4450
4488
multiprocessing .get_logger ().setLevel (LOG_LEVEL )
4451
4489
4452
4490
def tearDownModule ():
4491
+ need_sleep = False
4492
+
4493
+ # bpo-26762: Some multiprocessing objects like Pool create reference
4494
+ # cycles. Trigger a garbage collection to break these cycles.
4495
+ test .support .gc_collect ()
4496
+
4453
4497
multiprocessing .set_start_method (old_start_method [0 ], force = True )
4454
4498
# pause a bit so we don't get warning about dangling threads/processes
4455
- time .sleep (0.5 )
4499
+ processes = set (multiprocessing .process ._dangling ) - set (dangling [0 ])
4500
+ if processes :
4501
+ need_sleep = True
4502
+ print ('Warning -- Dangling processes: %s' % processes ,
4503
+ file = sys .stderr )
4504
+ processes = None
4505
+
4506
+ threads = set (threading ._dangling ) - set (dangling [1 ])
4507
+ if threads :
4508
+ need_sleep = True
4509
+ print ('Warning -- Dangling threads: %s' % threads ,
4510
+ file = sys .stderr )
4511
+ threads = None
4512
+
4513
+ # Sleep 500 ms to give time to child processes to complete.
4514
+ if need_sleep :
4515
+ time .sleep (0.5 )
4456
4516
multiprocessing .process ._cleanup ()
4457
- gc .collect ()
4458
- tmp = set (multiprocessing .process ._dangling ) - set (dangling [0 ])
4459
- if tmp :
4460
- print ('Dangling processes:' , tmp , file = sys .stderr )
4461
- del tmp
4462
- tmp = set (threading ._dangling ) - set (dangling [1 ])
4463
- if tmp :
4464
- print ('Dangling threads:' , tmp , file = sys .stderr )
4517
+ test .support .gc_collect ()
4465
4518
4466
4519
remote_globs ['setUpModule' ] = setUpModule
4467
4520
remote_globs ['tearDownModule' ] = tearDownModule
0 commit comments