Skip to content

Commit d53fab6

Browse files
committed
[driver] Serialize load_binary to prevent IGC race condition (#7581)
IGC 2.38.x has a race condition in its in-process Level Zero JIT path (`zeModuleCreate`). When multiple Triton kernels JIT-compile concurrently in the same process (via AsyncCompileMode's ThreadPoolExecutor), each thread calls `load_binary`, which releases the Python GIL on entry to the C extension and then calls `zeModuleCreate`. With the GIL released, multiple threads can be inside `zeModuleCreate` simultaneously, corrupting IGC's internal state and triggering a `SIGABRT` in `libigdrcl.so`. The crash is confirmed by the definitive test: adding any compilation overhead that serializes the calls (`IGC_PrintToConsole=1`, shader dump) prevents it entirely — proving a timing race, not a deterministic bug. Fix: add a module-level `threading.Lock` that serializes all `load_binary` calls. This ensures only one thread is inside `zeModuleCreate` at a time, preventing the race without requiring an IGC fix. Performance: verified +1.7% startup time difference (within measurement noise) on Arc A770 with vLLM. Hot-path inference is unaffected (kernels are cached after first compilation). Fixes: #7581
1 parent 0f45edf commit d53fab6

1 file changed

Lines changed: 21 additions & 10 deletions

File tree

third_party/intel/backend/driver.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import subprocess
1010
import sysconfig
1111
import tempfile
12+
import threading
1213
from pathlib import Path
1314
from functools import cached_property, lru_cache
1415

@@ -26,6 +27,12 @@
2627
# and via setdefault so an explicit choice by the user still wins.
2728
os.environ.setdefault("PTI_COLLECTION_MODE", "0")
2829

30+
# IGC 2.38.x has a race condition in its in-process Level Zero JIT path (zeModuleCreate).
31+
# Concurrent calls from different threads in the same process can trigger an internal
32+
# compiler error (SIGABRT in libigdrcl.so). Serializing all load_binary calls prevents
33+
# the race without requiring an IGC fix. See issue #7581.
34+
_load_binary_lock = threading.Lock()
35+
2936
# A hard-coded cache version that can be updated when we know that the cached file is invalid and
3037
# there are no other ways to detect that the runtime environment has changed. For example, a shared
3138
# library has been updated as a result of updated dependencies.
@@ -307,16 +314,20 @@ def load_binary(self, *args):
307314
# IGC build log carries a PTSS marker, OutOfResources is raised
308315
# directly so triton.runtime.autotuner can skip the offending tile.
309316
# No per-launch overhead on the success path.
310-
try:
311-
return self.shared_library.load_binary(args)
312-
except Exception as e:
313-
from triton.runtime.errors import IntelGPUError, OutOfResources
314-
if isinstance(e, OutOfResources):
315-
raise
316-
if str(e).startswith("ZE_"):
317-
raise IntelGPUError("Error during Intel load_binary: " + str(e)) from e
318-
else:
319-
raise e
317+
#
318+
# _load_binary_lock serializes concurrent zeModuleCreate calls to
319+
# work around a race condition in IGC 2.38.x in-process JIT. See #7581.
320+
with _load_binary_lock:
321+
try:
322+
return self.shared_library.load_binary(args)
323+
except Exception as e:
324+
from triton.runtime.errors import IntelGPUError, OutOfResources
325+
if isinstance(e, OutOfResources):
326+
raise
327+
if str(e).startswith("ZE_"):
328+
raise IntelGPUError("Error during Intel load_binary: " + str(e)) from e
329+
else:
330+
raise e
320331

321332
if os.name != 'nt':
322333

0 commit comments

Comments
 (0)