Source code for hidet.ffi.shared_lib
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import ctypes
from typing import Dict
# retrieve the dlclose function
_dlclose = ctypes.CDLL(None).dlclose # None indicates the main program
_dlclose.argtypes = [ctypes.c_void_p]
_dlclose.rettype = ctypes.c_int
[docs]class SharedLibrary:
"""
Manage the loaded dynamic libraries.
**Why we need this module?**
The ctypes.CDLL class does not provide a way to unload the loaded library. When a library is loaded, it will never
be unloaded until the program exits. However, when we tune an operator, we need to generate hundreds of kernels, and
each kernel will be compiled into a shared library. If we do not unload the shared library, we would load tens of
thousands of shared libraries, which will trigger memory error like (I also see other error messages):
| "cannot apply additional memory protection after relocation"
To solve this problem, we need to unload the shared library after we use it. Thus, whenever we need to load a shared
library, we should use this module instead of the ctypes.CDLL class. The SharedLibrary class will keep track of the
loaded libraries, and unload them when no one references them.
The current implementation only supports Linux systems. Will add support for Windows when we plan to support Windows
in the project-level.
**Usage**
.. code-block::
lib = SharedLibrary('./libhidet.so')
func = lib['func_name']
del lib
# at this point, the lib will not be unloaded because we still
# hold a reference to a function in the library.
del func
# the 'libhidet.so' will be unloaded via dlclose after the last
# reference to it or its functions are deleted.
Parameters
----------
lib_path: str
The path to the shared library (e.g., './lib.so')
"""
loaded_cdll_libraries: Dict[str, ctypes.CDLL] = {}
reference_count: Dict[str, int] = {}
def __init__(self, lib_path: str):
self.lib_path: str = lib_path
cls = SharedLibrary
if lib_path in cls.loaded_cdll_libraries:
self.cdll: ctypes.CDLL = cls.loaded_cdll_libraries[lib_path]
cls.reference_count[lib_path] += 1
else:
cdll = ctypes.CDLL(lib_path)
self.cdll: ctypes.CDLL = cdll
cls.loaded_cdll_libraries[lib_path] = cdll
cls.reference_count[lib_path] = 1
def __getitem__(self, item):
"""
Get the function from the loaded library.
Parameters
----------
item: str
The name of the function.
Returns
-------
func: ctypes.CFUNCTYPE
The loaded function.
"""
ret = self.cdll[item]
# keep a reference to the library to prevent it from being unloaded before the function is deleted.
ret._lib = self
return ret
def __getattr__(self, item):
return self[item]
def __del__(self):
self.reference_count[self.lib_path] -= 1
if self.reference_count[self.lib_path] == 0:
del self.loaded_cdll_libraries[self.lib_path]
del self.reference_count[self.lib_path]
_dlclose(self.cdll._handle)
del self.cdll