diff --git a/tests/cpp/util/CMakeLists.txt b/tests/cpp/util/CMakeLists.txt index 1dfd2fed4e4..b80a588fe35 100644 --- a/tests/cpp/util/CMakeLists.txt +++ b/tests/cpp/util/CMakeLists.txt @@ -21,4 +21,6 @@ target_link_libraries(test_util PUBLIC target_compile_options(test_util PRIVATE -O2 -fopenmp) include(GoogleTest) -gtest_discover_tests(test_util DISCOVERY_TIMEOUT 600) +# MALLOC_PERTURB_ makes glibc overwrite freed memory, so a destructor that reads a destroyed object +# at exit crashes the test process, which ctest reports as a failure, instead of passing silently. +gtest_discover_tests(test_util DISCOVERY_TIMEOUT 600 PROPERTIES ENVIRONMENT "MALLOC_PERTURB_=165") diff --git a/transformer_engine/common/fused_attn/graph_cache.h b/transformer_engine/common/fused_attn/graph_cache.h index b3020f25acf..d52a0daf70b 100644 --- a/transformer_engine/common/fused_attn/graph_cache.h +++ b/transformer_engine/common/fused_attn/graph_cache.h @@ -130,7 +130,9 @@ std::shared_ptr> cache_graph(GraphCache auto get_graph(const FusedAttnConfig &cfg, cudnnHandle_t handle) { - static GraphCache cache; + // Never destroyed: destroying cached cuDNN plans calls into CUDA, which must not be used during + // program termination. + static auto &cache = *new GraphCache; cfg.check_derived(); return detail::cache_graph(cache, cfg.make_cache_key(kPass), kBackend, kPass, handle, [&] { return kCreateGraphFn(cfg); }); diff --git a/transformer_engine/common/util/cuda_driver.h b/transformer_engine/common/util/cuda_driver.h index 16242347f12..3b85bd7cce4 100644 --- a/transformer_engine/common/util/cuda_driver.h +++ b/transformer_engine/common/util/cuda_driver.h @@ -40,8 +40,10 @@ template inline CUresult call(const char *symbol, ArgTs... args) { using FuncT = CUresult(ArgTs...); - static std::unordered_map symbol_cache; - static std::mutex cache_mutex; + // Never destroyed, so a lookup never reads a destroyed map, whatever order statics are + // constructed and destroyed in. + static auto &symbol_cache = *new std::unordered_map(); + static auto &cache_mutex = *new std::mutex(); FuncT *func; { diff --git a/transformer_engine/common/util/rtc.cpp b/transformer_engine/common/util/rtc.cpp index 2ffbbf72334..f591620124c 100644 --- a/transformer_engine/common/util/rtc.cpp +++ b/transformer_engine/common/util/rtc.cpp @@ -153,7 +153,9 @@ int Kernel::occupancy_max_active_blocks_per_sm(int device_id, int block_size, KernelManager& KernelManager::instance() { NVTE_CHECK(is_enabled(), "NVRTC support is not enabled"); - static KernelManager instance_; + // Never destroyed: ~Kernel unloads modules through the CUDA driver, and CUDA must not be used + // during program termination. + static KernelManager& instance_ = *new KernelManager; return instance_; }