test_multiprocessing detects dangling per test case (#2841) · python/cpython@ffb4940

@@ -4303,7 +4303,32 @@ def test_empty(self):

43034303

# Mixins

43044304

#

430543054306-

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)

43274352432843534329-

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):

4350437543514376

@classmethod

43524377

def setUpClass(cls):

4378+

super().setUpClass()

43534379

cls.manager = multiprocessing.Manager()

4354438043554381

@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

437444104411+

super().tearDownClass()

4412+437544134376-

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)

4451448944524490

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()

4465451844664519

remote_globs['setUpModule'] = setUpModule

44674520

remote_globs['tearDownModule'] = tearDownModule