Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9a8d1d7

Browse files
authored
bpo-35424: emit ResourceWarning at multiprocessing.Pool destruction (GH-10974)
multiprocessing.Pool destructor now emits ResourceWarning if the pool is still running.
1 parent c5d5dfd commit 9a8d1d7

3 files changed

Lines changed: 36 additions & 5 deletions

File tree

Lib/multiprocessing/pool.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313
# Imports
1414
#
1515

16-
import threading
17-
import queue
18-
import itertools
1916
import collections
17+
import itertools
2018
import os
19+
import queue
20+
import threading
2121
import time
2222
import traceback
23+
import warnings
2324

2425
# If threading is available then ThreadPool should be provided. Therefore
2526
# we avoid top-level imports which are liable to fail on some systems.
@@ -30,6 +31,7 @@
3031
# Constants representing the state of a pool
3132
#
3233

34+
INIT = "INIT"
3335
RUN = "RUN"
3436
CLOSE = "CLOSE"
3537
TERMINATE = "TERMINATE"
@@ -154,11 +156,15 @@ def Process(self, *args, **kwds):
154156

155157
def __init__(self, processes=None, initializer=None, initargs=(),
156158
maxtasksperchild=None, context=None):
159+
# Attributes initialized early to make sure that they exist in
160+
# __del__() if __init__() raises an exception
161+
self._pool = []
162+
self._state = INIT
163+
157164
self._ctx = context or get_context()
158165
self._setup_queues()
159166
self._taskqueue = queue.SimpleQueue()
160167
self._cache = {}
161-
self._state = RUN
162168
self._maxtasksperchild = maxtasksperchild
163169
self._initializer = initializer
164170
self._initargs = initargs
@@ -172,7 +178,6 @@ def __init__(self, processes=None, initializer=None, initargs=(),
172178
raise TypeError('initializer must be a callable')
173179

174180
self._processes = processes
175-
self._pool = []
176181
try:
177182
self._repopulate_pool()
178183
except Exception:
@@ -216,6 +221,14 @@ def __init__(self, processes=None, initializer=None, initargs=(),
216221
self._result_handler, self._cache),
217222
exitpriority=15
218223
)
224+
self._state = RUN
225+
226+
# Copy globals as function locals to make sure that they are available
227+
# during Python shutdown when the Pool is destroyed.
228+
def __del__(self, _warn=warnings.warn, RUN=RUN):
229+
if self._state == RUN:
230+
_warn(f"unclosed running multiprocessing pool {self!r}",
231+
ResourceWarning, source=self)
219232

220233
def __repr__(self):
221234
cls = self.__class__

Lib/test/_test_multiprocessing.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2577,6 +2577,22 @@ def test_enter(self):
25772577
pass
25782578
pool.join()
25792579

2580+
def test_resource_warning(self):
2581+
if self.TYPE == 'manager':
2582+
self.skipTest("test not applicable to manager")
2583+
2584+
pool = self.Pool(1)
2585+
pool.terminate()
2586+
pool.join()
2587+
2588+
# force state to RUN to emit ResourceWarning in __del__()
2589+
pool._state = multiprocessing.pool.RUN
2590+
2591+
with support.check_warnings(('unclosed running multiprocessing pool',
2592+
ResourceWarning)):
2593+
pool = None
2594+
support.gc_collect()
2595+
25802596

25812597
def raising():
25822598
raise KeyError("key")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:class:`multiprocessing.Pool` destructor now emits :exc:`ResourceWarning`
2+
if the pool is still running.

0 commit comments

Comments
 (0)