[libsycl] Fix for static vars deinit order (libsycl vs liboffload) (#181366)
both libsycl & liboffload uses static variables. on Linux static variable destructor is called earlier than the method with `__attribute__((destructor(...)))`. this fix helps to avoid crash due to liboffload static variable early destruction. the approach utilizes the following rule "For each local object obj with static storage duration, obj is destroyed as if a function calling the destructor of obj were registered with [std::atexit](https://en.cppreference.com/w/cpp/utility/program/atexit.html) at the completion of the constructor of obj." from `std::exit`. in the first call of get_platforms we call liboffload's iterateDevices that leads to liboffload static storage initialization. Then we initialize our own local static var after this to be able to call our shutdown methods earlier and before the liboffload objects are destructed at the end of program. Important note: SYCL RT follows SYCL 2020 specification that doesn't declare any init/shutdown methods that can help to avoid usage of static variables. --------- Signed-off-by: Tikhomirova, Kseniya <kseniya.tikhomirova@intel.com>
This commit is contained in:
parent
9f2351c27e
commit
3e2fb2e7cb
@ -18,6 +18,34 @@
|
||||
_LIBSYCL_BEGIN_NAMESPACE_SYCL
|
||||
namespace detail {
|
||||
|
||||
the approach utilizes the following rule
|
||||
"For each local object obj with static storage duration, obj is destroyed "
|
||||
"as if a function calling the destructor of obj were registered with "
|
||||
"std::atexit at the completion of the constructor of obj." from.
|
||||
|
||||
// libsycl follows SYCL 2020 specification that doesn't declare any
|
||||
// init/shutdown methods that can help to avoid usage of static variables.
|
||||
// liboffload uses static variables too. In the first call of get_platforms
|
||||
// we call liboffload's iterateDevices that leads to liboffload static
|
||||
// storage initialization. Then we initialize our own local static var of
|
||||
// StaticVarShutdownHandler type to be able to call our shutdown methods
|
||||
// earlier and before the liboffload objects are destructed at the end of
|
||||
// program. See documentation of std::exit for local objects with static
|
||||
// storage duration.
|
||||
struct StaticVarShutdownHandler {
|
||||
StaticVarShutdownHandler(const StaticVarShutdownHandler &) = delete;
|
||||
StaticVarShutdownHandler &
|
||||
operator=(const StaticVarShutdownHandler &) = delete;
|
||||
~StaticVarShutdownHandler() {
|
||||
// No error reporting in shutdown
|
||||
std::ignore = olShutDown();
|
||||
}
|
||||
};
|
||||
|
||||
void registerStaticVarShutdownHandler() {
|
||||
static StaticVarShutdownHandler handler{};
|
||||
}
|
||||
|
||||
std::vector<detail::OffloadTopology> &getOffloadTopologies() {
|
||||
static std::vector<detail::OffloadTopology> Topologies(
|
||||
OL_PLATFORM_BACKEND_LAST);
|
||||
@ -29,43 +57,5 @@ std::vector<PlatformImplUPtr> &getPlatformCache() {
|
||||
return PlatformCache;
|
||||
}
|
||||
|
||||
static void shutdown() {
|
||||
// No error reporting in shutdown
|
||||
std::ignore = olShutDown();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
extern "C" _LIBSYCL_EXPORT BOOL WINAPI DllMain(HINSTANCE hinstDLL,
|
||||
DWORD fdwReason,
|
||||
LPVOID lpReserved) {
|
||||
// Perform actions based on the reason for calling.
|
||||
switch (fdwReason) {
|
||||
case DLL_PROCESS_DETACH:
|
||||
try {
|
||||
shutdown();
|
||||
} catch (std::exception &e) {
|
||||
// TODO: Investigate how to handle and report errors that occur during
|
||||
// shutdown.
|
||||
}
|
||||
|
||||
break;
|
||||
case DLL_PROCESS_ATTACH:
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
break;
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE; // Successful DLL_PROCESS_ATTACH.
|
||||
}
|
||||
#else
|
||||
|
||||
// `syclUnload()` is declared as a low priority destructor to ensure it runs
|
||||
// after all other global destructors. Priorities 0-100 are reserved for use
|
||||
// by the compiler and C and C++ standard libraries. SYCL applications may use
|
||||
// priorities in the range 101-109 to schedule destructors to run after libsycl
|
||||
// finalization.
|
||||
__attribute__((destructor(110))) static void syclUnload() { shutdown(); }
|
||||
#endif
|
||||
} // namespace detail
|
||||
_LIBSYCL_END_NAMESPACE_SYCL
|
||||
|
||||
@ -36,6 +36,10 @@ std::vector<detail::OffloadTopology> &getOffloadTopologies();
|
||||
/// \returns std::vector of implementation objects for all platforms.
|
||||
std::vector<std::unique_ptr<PlatformImpl>> &getPlatformCache();
|
||||
|
||||
// This initializes a function-local variable whose destructor is invoked as
|
||||
// the SYCL shared library is first being unloaded.
|
||||
void registerStaticVarShutdownHandler();
|
||||
|
||||
} // namespace detail
|
||||
_LIBSYCL_END_NAMESPACE_SYCL
|
||||
|
||||
|
||||
@ -38,6 +38,8 @@ const std::vector<PlatformImplUPtr> &PlatformImpl::getPlatforms() {
|
||||
[[maybe_unused]] static auto InitPlatformsOnce = []() {
|
||||
discoverOffloadDevices();
|
||||
|
||||
registerStaticVarShutdownHandler();
|
||||
|
||||
auto &PlatformCache = getPlatformCache();
|
||||
for (const auto &Topo : getOffloadTopologies()) {
|
||||
size_t PlatformIndex = 0;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user