Skip to content

Fix Windows ROCm hang on Translator destruction and DLL loading, build with ROCm 10.0 - #2106

Merged
jordimas merged 1 commit into
OpenNMT:masterfrom
0xDELUXA:windows-rocm-fixes
Oct 12, 2026
Merged

jordimas merged 1 commit into
OpenNMT:masterfrom
0xDELUXA:windows-rocm-fixes

Conversation

@0xDELUXA

@0xDELUXA 0xDELUXA commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #2101.

On Windows ROCm wheels, OpenMP is not detected by CMake, so parallel_for falls back to a static thread_local BS::thread_pool. When the replica worker exits, the pool is destroyed under the loader lock while its threads also need that lock, causing Translator destruction to hang after multi-threaded CPU work. The fix mirrors #2076: the pool is heap-allocated on Windows and freed from destroy_context() in ReplicaWorker::finalize(). test_shutdown_does_not_deadlock now passes on ROCm.

Also fixes ROCm DLL discovery. __init__.py now supports _rocm_sdk_libraries, _rocm_sdk_libraries_gfx*, and the older _rocm_sdk_libraries_custom package. HIP_PATH\bin is used as a fallback only when no ROCm pip package is installed.

The Windows ROCm wheel is now built against ROCm 10.0.0 instead of 7.2, avoiding a ROCm 7.2 runtime exit hang.

Tested on Windows 11 with an RX 9060 XT (gfx1200) on ROCm 7.2, 7.14, and 10.0. Translator destruction now returns in under 20 ms instead of hanging, GPU translation works, and the Python test suite passes aside from tests requiring unavailable data or CUDA-enabled torch.

@jordimas

jordimas commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

Could we also clean up the calling thread’s pool after model loading, including on exceptions? Weight conversion can invoke parallel_for, but that thread never runs ReplicaWorker::finalize().

A regression test that loads a model requiring weight conversion on temporary threads and checks that the thread count returns to baseline would cover this path.

@0xDELUXA
0xDELUXA force-pushed the windows-rocm-fixes branch from a191c51 to b299796 Compare October 5, 2026 20:47
@0xDELUXA

0xDELUXA commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Good catch, thanks. The calling thread's pool also gets recreated after load(), because ReplicaWorker's constructor builds the replica on that thread via Replica::create_from_model. A CallingThreadContextGuard in replica_pool.h now calls destroy_context(Device::CPU) on scope exit (so also on exceptions) around model_loader.load(), replica creation in initialize_pool, set_models, and the Python load_model path, which calls load() directly.

Added test_model_load_does_not_leak_threads: it loads a float32 model with compute_type="int8" on CPU five times, each on a fresh thread, in a subprocess, and checks that the thread count returns to the baseline (with a small margin for an OpenMP team). Without the fix it fails with 20 leaked threads, 4 per load; with it, it passes. It also passes against the 4.8.2 wheel from PyPI.

The test pins MKL_NUM_THREADS=1 so that only threads owned by CTranslate2 are counted. A newer libiomp5md.dll (file version 20260213) leaks the OpenMP team started from a short-lived thread, about 9 threads per load through MKL, and still 3 per load from CTranslate2's own parallel regions when the ROCm build is compiled with OpenMP. The oneAPI 2025.3 runtime bundled in the current wheels (20250910) does not leak, so this only matters if the bundled runtime is bumped.

@0xDELUXA
0xDELUXA force-pushed the windows-rocm-fixes branch from b299796 to a63554a Compare October 5, 2026 22:08
@0xDELUXA
0xDELUXA force-pushed the windows-rocm-fixes branch from a63554a to f6fa443 Compare October 6, 2026 08:40
@0xDELUXA

Copy link
Copy Markdown
Contributor Author

@jordimas friendly ping: the calling-thread pool cleanup and the thread-leak regression test from your review are in f6fa443, and CI is green. Let me know if anything else is needed.

@jordimas

Copy link
Copy Markdown
Collaborator

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ROCm/HIP Windows wheel: Translator destruction hangs indefinitely, even in CPU-only mode with no AMD GPU present

2 participants