gh-104090: Add exit code to multiprocessing ResourceTracker (GH-115410) · python/cpython@4a9e649

GitHub

@@ -29,8 +29,12 @@

2929_HAVE_SIGMASK=hasattr(signal, 'pthread_sigmask')

3030_IGNORED_SIGNALS= (signal.SIGINT, signal.SIGTERM)

313132+defcleanup_noop(name):

33+raiseRuntimeError('noop should never be registered or cleaned up')

34+3235_CLEANUP_FUNCS= {

33-'noop': lambda: None,

36+'noop': cleanup_noop,

37+'dummy': lambdaname: None, # Dummy resource used in tests

3438}

35393640ifos.name=='posix':

@@ -61,6 +65,7 @@ def __init__(self):

6165self._lock=threading.RLock()

6266self._fd=None

6367self._pid=None

68+self._exitcode=None

64696570def_reentrant_call_error(self):

6671# gh-109629: this happens if an explicit call to the ResourceTracker

@@ -84,9 +89,16 @@ def _stop(self):

8489os.close(self._fd)

8590self._fd=None

869187-os.waitpid(self._pid, 0)

92+_, status=os.waitpid(self._pid, 0)

93+8894self._pid=None

899596+try:

97+self._exitcode=os.waitstatus_to_exitcode(status)

98+exceptValueError:

99+# os.waitstatus_to_exitcode may raise an exception for invalid values

100+self._exitcode=None

101+90102defgetfd(self):

91103self.ensure_running()

92104returnself._fd

@@ -119,6 +131,7 @@ def ensure_running(self):

119131pass

120132self._fd=None

121133self._pid=None

134+self._exitcode=None

122135123136warnings.warn('resource_tracker: process died unexpectedly, '

124137'relaunching. Some resources might leak.')

@@ -221,6 +234,8 @@ def main(fd):

221234pass

222235223236cache= {rtype: set() forrtypein_CLEANUP_FUNCS.keys()}

237+exit_code=0

238+224239try:

225240# keep track of registered/unregistered resources

226241withopen(fd, 'rb') asf:

@@ -242,6 +257,7 @@ def main(fd):

242257else:

243258raiseRuntimeError('unrecognized command %r'%cmd)

244259exceptException:

260+exit_code=3

245261try:

246262sys.excepthook(*sys.exc_info())

247263except:

@@ -251,10 +267,17 @@ def main(fd):

251267forrtype, rtype_cacheincache.items():

252268ifrtype_cache:

253269try:

254-warnings.warn(

255-f'resource_tracker: There appear to be {len(rtype_cache)} '

256-f'leaked {rtype} objects to clean up at shutdown: {rtype_cache}'

257- )

270+exit_code=1

271+ifrtype=='dummy':

272+# The test 'dummy' resource is expected to leak.

273+# We skip the warning (and *only* the warning) for it.

274+pass

275+else:

276+warnings.warn(

277+f'resource_tracker: There appear to be '

278+f'{len(rtype_cache)} leaked {rtype} objects to '

279+f'clean up at shutdown: {rtype_cache}'

280+ )

258281exceptException:

259282pass

260283fornameinrtype_cache:

@@ -265,6 +288,9 @@ def main(fd):

265288try:

266289_CLEANUP_FUNCS[rtype](name)

267290exceptExceptionase:

291+exit_code=2

268292warnings.warn('resource_tracker: %r: %s'% (name, e))

269293finally:

270294pass

295+296+sys.exit(exit_code)