Skip to content

Commit c115df9

Browse files
Updated Reloading Scheme for ReloadableDLL (spcl#2218)
Modified the reloading scheme used by `ReloadableDLL`. If the library (of the compiled SDFG) is already loaded, through another instance of `CompiledSDFG` then `ReloadableDLL` will copy the SDFG library and try to load that until it founds a name that is free. In ICON4Py we noticed that this leads sometime to a segmentation fault on Linux, but not on MacOS X. We traced the main issue down to the fact that `ReloadableDLL` created a copy of the SDFG library without checking if the new name is already used, instead the file is simply overwritten. The new scheme changes this slightly, in the following ways: - If the new name is already taken, then no copy is performed and the class tries to use that file, that already exists. - Instead of copying library `n - 1` to `n` it always makes a copy from the initial library. --------- Co-authored-by: Philipp Schaad <schaad.phil@gmail.com>
1 parent 935cfb6 commit c115df9

1 file changed

Lines changed: 32 additions & 5 deletions

File tree

dace/codegen/compiled_sdfg.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import warnings
1010
import tempfile
1111
import pickle
12+
import pathlib
1213
import sys
1314

1415
import numpy as np
@@ -77,7 +78,8 @@ def is_loaded(self) -> bool:
7778
lib_cfilename = ctypes.c_wchar_p(self._library_filename)
7879
else:
7980
# As UTF-8
80-
lib_cfilename = ctypes.c_char_p(self._library_filename.encode('utf-8'))
81+
tt = self._library_filename.encode('utf-8')
82+
lib_cfilename = ctypes.c_char_p(tt)
8183

8284
return self._stub.is_library_loaded(lib_cfilename) == 1
8385

@@ -96,28 +98,47 @@ def load(self):
9698
# Check if library is already loaded
9799
is_loaded = True
98100
lib_cfilename = None
101+
lib_filename = self._library_filename
102+
counter = 0
99103
while is_loaded:
100104
# Convert library filename to string according to OS
101105
if os.name == 'nt':
102106
# As UTF-16
103-
lib_cfilename = ctypes.c_wchar_p(self._library_filename)
107+
lib_cfilename = ctypes.c_wchar_p(lib_filename)
104108
else:
105109
# As UTF-8
106-
lib_cfilename = ctypes.c_char_p(self._library_filename.encode('utf-8'))
110+
lib_cfilename = ctypes.c_char_p(lib_filename.encode('utf-8'))
107111

112+
# Test if the library is loaded.
108113
is_loaded = self._stub.is_library_loaded(lib_cfilename)
114+
109115
if is_loaded == 1:
110116
warnings.warn(f'Library {self._library_filename} already loaded, renaming file')
117+
118+
# The library is loaded, copy the _original_ library file to a new file
119+
# and then try to load that. We only do the copy if the new new name is
120+
# free. It seems that at least on LINUX there is some issue if we
121+
# overwrite a file that already exists.
122+
lib_filename = self._library_filename + f'_{counter}'
123+
counter += 1
124+
if pathlib.Path(lib_filename).exists():
125+
assert pathlib.Path(lib_filename).is_file()
126+
continue
127+
128+
# The file name is not taken, so make a copy. There might be a race condition
129+
# here in the presence of multiple processes.
130+
# TODO: Investigate if we should switch to hardlinks if they are supported.
111131
try:
112-
shutil.copyfile(self._library_filename, self._library_filename + '_')
113-
self._library_filename += '_'
132+
assert self._library_filename != lib_filename
133+
shutil.copyfile(self._library_filename, lib_filename)
114134
except shutil.Error:
115135
raise cgx.DuplicateDLLError(f'Library {os.path.basename(self._library_filename)}'
116136
'is already loaded somewhere else and cannot be unloaded. '
117137
'Please use a different name for the SDFG/program.')
118138

119139
# Actually load the library
120140
self._lib = ctypes.c_void_p(self._stub.load_library(lib_cfilename))
141+
self._library_filename = lib_filename
121142

122143
if self._lib.value is None:
123144
# Try to understand why the library is not loading, if dynamic
@@ -147,6 +168,12 @@ def __enter__(self, *args, **kwargs):
147168
def __exit__(self, *args, **kwargs):
148169
self.unload()
149170

171+
def __copy__(self):
172+
raise RuntimeError(f'Can not copy ReloadableDLL({self._library_filename})')
173+
174+
def __deepcopy__(self, memodict={}):
175+
raise RuntimeError(f'Can not copy ReloadableDLL({self._library_filename})')
176+
150177

151178
class CompiledSDFG(object):
152179
""" A compiled SDFG object that can be called through Python.

0 commit comments

Comments
 (0)