@@ -4303,7 +4303,32 @@ def test_empty(self):
43034303# Mixins
43044304#
43054305
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 ):
43074332 TYPE = 'processes'
43084333 Process = multiprocessing .Process
43094334 connection = multiprocessing .connection
@@ -4326,7 +4351,7 @@ class ProcessesMixin(object):
43264351 RawArray = staticmethod (multiprocessing .RawArray )
43274352
43284353
4329- class ManagerMixin (object ):
4354+ class ManagerMixin (BaseMixin ):
43304355 TYPE = 'manager'
43314356 Process = multiprocessing .Process
43324357 Queue = property (operator .attrgetter ('manager.Queue' ))
@@ -4350,30 +4375,43 @@ def Pool(cls, *args, **kwds):
43504375
43514376 @classmethod
43524377 def setUpClass (cls ):
4378+ super ().setUpClass ()
43534379 cls .manager = multiprocessing .Manager ()
43544380
43554381 @classmethod
43564382 def tearDownClass (cls ):
43574383 # only the manager process should be returned by active_children()
43584384 # but this can take a bit on slow machines, so wait a few seconds
43594385 # if there are other children too (see #17395)
4386+ start_time = time .monotonic ()
43604387 t = 0.01
4361- while len (multiprocessing .active_children ()) > 1 and t < 5 :
4388+ while len (multiprocessing .active_children ()) > 1 :
43624389 time .sleep (t )
43634390 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+
43644399 gc .collect () # do garbage collection
43654400 if cls .manager ._number_of_objects () != 0 :
43664401 # This is not really an error since some tests do not
43674402 # ensure that all processes which hold a reference to a
43684403 # 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:' )
43704406 print (cls .manager ._debug_info ())
43714407 cls .manager .shutdown ()
43724408 cls .manager .join ()
43734409 cls .manager = None
43744410
4411+ super ().tearDownClass ()
4412+
43754413
4376- class ThreadsMixin (object ):
4414+ class ThreadsMixin (BaseMixin ):
43774415 TYPE = 'threads'
43784416 Process = multiprocessing .dummy .Process
43794417 connection = multiprocessing .dummy .connection
@@ -4450,18 +4488,33 @@ def setUpModule():
44504488 multiprocessing .get_logger ().setLevel (LOG_LEVEL )
44514489
44524490 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+
44534497 multiprocessing .set_start_method (old_start_method [0 ], force = True )
44544498 # 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 )
44564516 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 ()
44654518
44664519 remote_globs ['setUpModule' ] = setUpModule
44674520 remote_globs ['tearDownModule' ] = tearDownModule
0 commit comments