From 8b56db8fd0dfe040ef7a5ce5e988c6149e317e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= Date: Thu, 18 Dec 2025 00:17:44 +0100 Subject: [PATCH 1/2] Add multiprocessing compatibility wrapper for macOS/Windows Add a Pool wrapper class that provides ProcessPoolExecutor-like API for multiprocess.Pool on darwin and win32 platforms. This ensures consistent interface across platforms by: - Accepting max_workers parameter (converted to processes) - Supporting context manager protocol (with statement) - Implementing map() with multiple iterables support - Providing close() and join() methods This allows code to use max_workers consistently on all platforms without platform-specific conditionals. The platform-specific multiprocessing implementation is necessary because: - On macOS/Windows: The multiprocess package (third-party) is used instead of standard multiprocessing due to reliability issues (see commits 803e4461, d56e216f) - On Linux: ProcessPoolExecutor from standard library is used - The compatibility layer bridges API differences between these implementations --- .../compatibility/multiprocessing.py | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/codechecker_common/compatibility/multiprocessing.py b/codechecker_common/compatibility/multiprocessing.py index 754294014d..75246bfad1 100644 --- a/codechecker_common/compatibility/multiprocessing.py +++ b/codechecker_common/compatibility/multiprocessing.py @@ -14,11 +14,60 @@ # pylint: disable=unused-import if sys.platform in ["darwin", "win32"]: from multiprocess import \ - Pipe, Pool, Process, \ + Pipe, Pool as _Pool, Process, \ Queue, \ Value, \ cpu_count from multiprocess.managers import SyncManager + + class Pool: + """ + Compatibility wrapper for multiprocess.Pool that accepts max_workers + parameter (like concurrent.futures.ProcessPoolExecutor) for consistency + across platforms. + """ + def __init__(self, max_workers=None, processes=None, initializer=None, + initargs=(), **kwargs): + if processes is None and max_workers is not None: + processes = max_workers + kwargs.pop('max_workers', None) + self._pool = _Pool(processes=processes, initializer=initializer, + initargs=initargs, **kwargs) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._pool.close() + self._pool.join() + return False + + def map(self, func, *iterables, **kwargs): + """ + Map function over iterables using the pool. + + This method mimics ProcessPoolExecutor.map() behavior which accepts + multiple iterables. When multiple iterables are provided, they are + zipped together and starmap is used to unpack the tuples. + + Note: 'timeout' parameter is not supported by multiprocess.Pool + and will be silently ignored if provided. + """ + pool_kwargs = {k: v for k, v in kwargs.items() if k != 'timeout'} + + if len(iterables) == 1: + return self._pool.map(func, iterables[0], **pool_kwargs) + else: + zipped = zip(*iterables) + return self._pool.starmap(func, zipped, **pool_kwargs) + + def close(self): + """Close the pool, preventing new tasks from being submitted.""" + self._pool.close() + + def join(self): + """Wait for worker processes to exit.""" + self._pool.join() else: from concurrent.futures import ProcessPoolExecutor as Pool from multiprocessing import \ From 9d2dd54c21c9c1008e5d96c48c364b8cd787e489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= Date: Fri, 19 Dec 2025 16:00:41 +0100 Subject: [PATCH 2/2] Ignore that we are using different types for this "compatibility import" --- codechecker_common/compatibility/multiprocessing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codechecker_common/compatibility/multiprocessing.py b/codechecker_common/compatibility/multiprocessing.py index 75246bfad1..218da713e9 100644 --- a/codechecker_common/compatibility/multiprocessing.py +++ b/codechecker_common/compatibility/multiprocessing.py @@ -69,7 +69,7 @@ def join(self): """Wait for worker processes to exit.""" self._pool.join() else: - from concurrent.futures import ProcessPoolExecutor as Pool + from concurrent.futures import ProcessPoolExecutor as Pool # type: ignore from multiprocessing import \ Pipe, \ Process, \