Expected Behavior
ProcessPoolExecutor in math scripts should safely create worker processes without risk of crashes on macOS, regardless of what libraries have been imported in the parent process.
Actual Behavior
Both math_base.py (safe_compute) and sympy_compute.py (safe_solve) use ProcessPoolExecutor with the default start method, which is fork on macOS. Forking after importing libraries like numpy, sqlite3, or the macOS Accelerate framework can cause:
- Segfaults / "Python quit unexpectedly" crashes
- Deadlocks from forked mutex state
- Corrupted state in child processes
This is a known macOS issue — Apple's Accelerate framework and other system libraries are not fork-safe.
Files Affected
scripts/cc_math/math_base.py — safe_compute() function
scripts/cc_math/sympy_compute.py — safe_solve() function
Fix Implemented
math_base.py
- Added
import multiprocessing
- Use
multiprocessing.get_context("spawn") and pass as mp_context to ProcessPoolExecutor
- Removed local
_wrapper closure (not picklable with spawn) — submit func directly with *args, **kwargs
# Before
with ProcessPoolExecutor(max_workers=1) as executor:
future = executor.submit(_wrapper)
# After
ctx = multiprocessing.get_context("spawn")
with ProcessPoolExecutor(max_workers=1, mp_context=ctx) as executor:
future = executor.submit(func, *args, **kwargs)
sympy_compute.py
- Added
import multiprocessing
- Use
multiprocessing.get_context("spawn") and pass as mp_context to ProcessPoolExecutor
- No closure issue here —
_solve_internal is already a module-level function
# Before
with ProcessPoolExecutor(max_workers=1) as executor:
future = executor.submit(_solve_internal, equation, var, domain)
# After
ctx = multiprocessing.get_context("spawn")
with ProcessPoolExecutor(max_workers=1, mp_context=ctx) as executor:
future = executor.submit(_solve_internal, equation, var, domain)
Testing
sympy_compute.py solve "x**2 - 4" → [-2, 2] ✓
safe_compute(math.factorial, 10) → 3628800 ✓
safe_solve("x**2 - 4") → [-2, 2] ✓
Note
The spawn start method is slightly slower than fork (it starts a fresh Python interpreter) but is the only safe option on macOS. It is already the default on Windows.
Expected Behavior
ProcessPoolExecutorin math scripts should safely create worker processes without risk of crashes on macOS, regardless of what libraries have been imported in the parent process.Actual Behavior
Both
math_base.py(safe_compute) andsympy_compute.py(safe_solve) useProcessPoolExecutorwith the default start method, which isforkon macOS. Forking after importing libraries like numpy, sqlite3, or the macOS Accelerate framework can cause:This is a known macOS issue — Apple's Accelerate framework and other system libraries are not fork-safe.
Files Affected
scripts/cc_math/math_base.py—safe_compute()functionscripts/cc_math/sympy_compute.py—safe_solve()functionFix Implemented
math_base.pyimport multiprocessingmultiprocessing.get_context("spawn")and pass asmp_contexttoProcessPoolExecutor_wrapperclosure (not picklable with spawn) — submitfuncdirectly with*args, **kwargssympy_compute.pyimport multiprocessingmultiprocessing.get_context("spawn")and pass asmp_contexttoProcessPoolExecutor_solve_internalis already a module-level functionTesting
sympy_compute.py solve "x**2 - 4"→[-2, 2]✓safe_compute(math.factorial, 10)→3628800✓safe_solve("x**2 - 4")→[-2, 2]✓Note
The
spawnstart method is slightly slower thanfork(it starts a fresh Python interpreter) but is the only safe option on macOS. It is already the default on Windows.