Skip to content

Commit c7b3d39

Browse files
committed
gh-83371: handle exceptions from user-supplied callbacks in process pools
User-supplied callbacks are called from an internal pool management thread. At present any exceptions they raise are not caught and so propagate out and kill the thread. This then causes problems for subsequent pool operations, including joining the pool hanging. As a QoL improvement, catch and handle any such exceptions using the system exception hook. Thus by default details of the exception will be printed to stderr, but the pool's integrity will remain intact.
1 parent 8a00c9a commit c7b3d39

File tree

1 file changed

+12
-4
lines changed

1 file changed

+12
-4
lines changed

Lib/multiprocessing/pool.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import itertools
1818
import os
1919
import queue
20+
import sys
2021
import threading
2122
import time
2223
import traceback
@@ -776,13 +777,20 @@ def get(self, timeout=None):
776777
def _set(self, i, obj):
777778
self._success, self._value = obj
778779
if self._callback and self._success:
779-
self._callback(self._value)
780+
self._handle_exceptions(self._callback, self._value)
780781
if self._error_callback and not self._success:
781-
self._error_callback(self._value)
782+
self._handle_exceptions(self._error_callback, self._value)
782783
self._event.set()
783784
del self._cache[self._job]
784785
self._pool = None
785786

787+
@staticmethod
788+
def _handle_exceptions(callback, args):
789+
try:
790+
return callback(args)
791+
except Exception as e:
792+
sys.excepthook(*sys.exc_info())
793+
786794
__class_getitem__ = classmethod(types.GenericAlias)
787795

788796
AsyncResult = ApplyResult # create alias -- see #17805
@@ -813,7 +821,7 @@ def _set(self, i, success_result):
813821
self._value[i*self._chunksize:(i+1)*self._chunksize] = result
814822
if self._number_left == 0:
815823
if self._callback:
816-
self._callback(self._value)
824+
self._handle_exceptions(self._callback, self._value)
817825
del self._cache[self._job]
818826
self._event.set()
819827
self._pool = None
@@ -825,7 +833,7 @@ def _set(self, i, success_result):
825833
if self._number_left == 0:
826834
# only consider the result ready once all jobs are done
827835
if self._error_callback:
828-
self._error_callback(self._value)
836+
self._handle_exceptions(self._error_callback, self._value)
829837
del self._cache[self._job]
830838
self._event.set()
831839
self._pool = None

0 commit comments

Comments
 (0)