Skip to content

Commit 73c43ae

Browse files
committed
fix(library): alternative implementation that cleans up using a finalizer
1 parent 8cf16e2 commit 73c43ae

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_linux.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,20 @@ def _load_libdl() -> ctypes.CDLL:
3838
LIBDL.dlerror.argtypes = []
3939
LIBDL.dlerror.restype = ctypes.c_char_p
4040

41+
LIBDL.dlclose.argtypes = [ctypes.c_void_p]
42+
LIBDL.dlclose.restype = ctypes.c_int
43+
4144
# First appeared in 2004-era glibc. Universally correct on Linux for all practical purposes.
4245
RTLD_DI_LINKMAP = 2
4346
RTLD_DI_ORIGIN = 6
4447

4548

49+
def unload_dl(handle: ctypes.c_void_p) -> None:
50+
result = LIBDL.dlclose(handle)
51+
if result:
52+
raise RuntimeError(LIBDL.dlerror())
53+
54+
4655
class _LinkMapLNameView(ctypes.Structure):
4756
"""
4857
Prefix-only view of glibc's `struct link_map` used **solely** to read `l_name`.

cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_windows.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
kernel32.AddDllDirectory.argtypes = [ctypes.wintypes.LPCWSTR]
4747
kernel32.AddDllDirectory.restype = ctypes.c_void_p # DLL_DIRECTORY_COOKIE
4848

49+
kernel32.FreeLibrary.argtypes = [ctypes.wintypes.HMODULE]
50+
kernel32.FreeLibrary.restype = ctypes.c_bool
51+
4952

5053
def ctypes_handle_to_unsigned_int(handle: ctypes.wintypes.HMODULE) -> int:
5154
"""Convert ctypes HMODULE to unsigned int."""
@@ -157,3 +160,9 @@ def load_with_abs_path(libname: str, found_path: str) -> LoadedDL:
157160
raise RuntimeError(f"Failed to load DLL at {found_path}: Windows error {error_code}")
158161

159162
return LoadedDL(found_path, False, ctypes_handle_to_unsigned_int(handle))
163+
164+
165+
def unload_dl(handle: ctypes.c_void_p) -> None:
166+
result = kernel32.FreeLibrary(handle)
167+
if not result:
168+
raise RuntimeError(f"Failed to load windows DLL with error code: {ctypes.GetLastError()}")

cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

4+
import ctypes
45
import functools
56
import struct
67
import sys
8+
import weakref
79

810
from cuda.pathfinder._dynamic_libs.find_nvidia_dynamic_lib import _FindNvidiaDynamicLib
911
from cuda.pathfinder._dynamic_libs.load_dl_common import LoadedDL, load_dependencies
@@ -14,12 +16,14 @@
1416
check_if_already_loaded_from_elsewhere,
1517
load_with_abs_path,
1618
load_with_system_search,
19+
unload_dl,
1720
)
1821
else:
1922
from cuda.pathfinder._dynamic_libs.load_dl_linux import (
2023
check_if_already_loaded_from_elsewhere,
2124
load_with_abs_path,
2225
load_with_system_search,
26+
unload_dl,
2327
)
2428

2529

@@ -117,4 +121,13 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL:
117121
f" Currently running: {pointer_size_bits}-bit Python"
118122
f" {sys.version_info.major}.{sys.version_info.minor}"
119123
)
120-
return _load_lib_no_cache(libname)
124+
125+
library = _load_lib_no_cache(libname)
126+
127+
# Ensure that the library is unloaded after GC runs on `library`
128+
#
129+
# We only need the address, so the rest of whatever is in `library` is free
130+
# to be cleaned up. The integer address is immutable, so it gets copied
131+
# upon being referenced here
132+
weakref.finalize(library, unload_dl, ctypes.c_void_p(library._handle_uint))
133+
return library

0 commit comments

Comments
 (0)