From a6275cea85a7be855cd0109a91cc15dda1fe3815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:33:03 -0400 Subject: [PATCH] CI: Count sequential grass.lib loads before the DLL-load crash (not for merge) A single, isolated `from grass.pygrass import utils` (which loads grass_gis.8.6.dll) does not crash (see the sibling ci-diag/standalone-import-test PR), but the same load crashes with "Windows fatal exception: code 0xc0000139" under pytest-xdist, and every dumpbin export/import diff of grass_gis.8.6.dll's dependency chain came back clean (see the other sibling diagnostic PRs). This points to a resource-count issue (e.g. a Windows/MinGW TLS-slot exhaustion, since libgomp and other mingw-w64 runtime DLLs use TLS) rather than a single missing symbol. This imports every grass.lib.* ctypes submodule one at a time in a single process, flushing progress after each import, to find exactly how many (and which) sequential loads it takes to reproduce the crash, or whether it doesn't reproduce at all in a single process regardless of count. Temporary, for CI investigation only. Co-Authored-By: Claude Sonnet 5 --- .../workflows/ci_diag_sequential_lib_load.py | 28 +++++++++++++++++++ .github/workflows/osgeo4w.yml | 18 ++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 .github/workflows/ci_diag_sequential_lib_load.py diff --git a/.github/workflows/ci_diag_sequential_lib_load.py b/.github/workflows/ci_diag_sequential_lib_load.py new file mode 100644 index 00000000000..920d4f13ea1 --- /dev/null +++ b/.github/workflows/ci_diag_sequential_lib_load.py @@ -0,0 +1,28 @@ +"""CI diagnostic, not for merge. + +Imports every grass.lib.* ctypes submodule one at a time in a single +process, flushing progress after each import, to find exactly how many +(and which) sequential ctypes library loads it takes to reproduce the +"Windows fatal exception: code 0xc0000139" crash seen under +pytest-xdist. A plain `python -c "from grass.pygrass import utils"` +in a single fresh process (see the sibling diagnostic PR) does not +crash, so this checks whether the crash is tied to the number of +distinct DLLs loaded cumulatively within one process. +""" + +import faulthandler +import pkgutil + +faulthandler.enable() + +import grass.lib # noqa: E402 + +modules = sorted(m.name for m in pkgutil.iter_modules(grass.lib.__path__)) +print(f"Found {len(modules)} grass.lib submodules: {modules}", flush=True) + +for i, name in enumerate(modules, 1): + print(f"[{i}/{len(modules)}] importing grass.lib.{name} ...", flush=True) + __import__(f"grass.lib.{name}") + print(f"[{i}/{len(modules)}] OK", flush=True) + +print("ALL grass.lib submodules imported successfully", flush=True) diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index 96b5a6bb9aa..4549ec77417 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -124,6 +124,24 @@ jobs: shell: msys2 {0} run: .github/workflows/test_simple.sh + - name: "[CI diagnostic, not for merge] Sequential grass.lib load count" + # A plain single import of grass.pygrass.utils (which loads + # grass_gis.8.6.dll) does not crash in isolation (see the sibling + # standalone-import-test PR), but the same load crashes with + # "Windows fatal exception: code 0xc0000139" under pytest-xdist. + # This imports every grass.lib.* ctypes submodule one at a time + # in a single process to see whether the crash is tied to the + # number of distinct DLLs accumulated as loaded in one process + # (e.g. a Windows/MinGW TLS-slot exhaustion), and if so, exactly + # how many/which imports it takes to trigger it. + run: | + call %OSGEO4W_ROOT%\opt\grass\etc\env.bat + set PYTHONPATH=%GISBASE%\etc\python;%PYTHONPATH% + path %GISBASE%\lib;%GISBASE%\bin;%PATH% + python .github/workflows/ci_diag_sequential_lib_load.py + # Obfuscated usage of GitHub Actions features, Windows CMD shell limits analysis + shell: cmd /D /E:ON /V:OFF /S /C "CALL C:/OSGeo4W/OSGeo4W.bat "{0}"" # zizmor: ignore[misfeature] + - name: Install additional python packages run: | python -m pip install ^