From 7da82e05d7188e1d96fcd8df57283c72c3c55076 Mon Sep 17 00:00:00 2001 From: "Neil R. Spruit" Date: Wed, 14 May 2025 14:02:43 -0700 Subject: [PATCH 01/10] Add ability to Register a TeardownCallback to notify release of L0 resources Signed-off-by: Neil R. Spruit --- include/loader/ze_loader.h | 14 ++++++++ source/lib/ze_lib.cpp | 73 ++++++++++++++++++++++++++++++++++++-- source/lib/ze_lib.h | 11 ++++++ 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/include/loader/ze_loader.h b/include/loader/ze_loader.h index dec810e1..7c49045a 100644 --- a/include/loader/ze_loader.h +++ b/include/loader/ze_loader.h @@ -94,6 +94,20 @@ zelEnableTracingLayer(); ZE_DLLEXPORT bool ZE_APICALL zelCheckIsLoaderInTearDown(); +/////////////////////////////////////////////////////////////////////////////// +/// @brief Exported function for registering a callback to indicate teardown. +/// @details The callback function will be invoked when the loader is in teardown. +/// +typedef void (*zel_loader_teardown_callback_t)(); +typedef void (*zel_application_teardown_callback_t)(uint32_t index); + +ZE_DLLEXPORT ze_result_t ZE_APICALL +zelRegisterTeardownCallback( + zel_loader_teardown_callback_t application_callback, // [in] Pointer to the user's application callback function + zel_application_teardown_callback_t *loader_callback, // [out] Pointer to the L0 Loader's callback function + uint32_t *index // [out] Index of the callback function +); + /////////////////////////////////////////////////////////////////////////////// /// @brief Exported function for Disabling the Tracing Layer During Runtime. /// diff --git a/source/lib/ze_lib.cpp b/source/lib/ze_lib.cpp index a470efee..82f778e9 100644 --- a/source/lib/ze_lib.cpp +++ b/source/lib/ze_lib.cpp @@ -27,7 +27,18 @@ namespace ze_lib } } bool delayContextDestruction = false; + bool loaderTeardownCallbackReceived = false; + void staticLoaderTeardownCallback() { + printf("ze_lib Static Teardown Callback\n"); + loaderTeardownCallbackReceived = true; + } #endif + void applicationTeardownCallback(uint32_t index) { + printf("ze_lib Application Teardown Callback %d\n", index); + if (ze_lib::context->teardownCallbacks.find(index) != ze_lib::context->teardownCallbacks.end()) { + ze_lib::context->teardownCallbacks.erase(index); + } + } bool destruction = false; /////////////////////////////////////////////////////////////////////////////// @@ -39,10 +50,22 @@ namespace ze_lib /////////////////////////////////////////////////////////////////////////////// __zedlllocal context_t::~context_t() { + if (debugTraceEnabled) { + debug_trace_message("ze_lib Context Destructor", ""); + } #ifdef DYNAMIC_LOAD_LOADER + if (!loaderTeardownCallbackReceived) { + loaderTeardownCallback(loaderTeardownCallbackIndex); + } if (loader) { FREE_DRIVER_LIBRARY( loader ); } +#else + // Call the teardown callbacks + for (auto &callback : teardownCallbacks) { + callback.second(); + } + teardownCallbacks.clear(); #endif ze_lib::destruction = true; }; @@ -339,9 +362,19 @@ namespace ze_lib isInitialized = true; } #ifdef DYNAMIC_LOAD_LOADER - if (!delayContextDestruction) { - std::atexit(context_at_exit_destructor); - } + std::call_once(ze_lib::context->initTeardownCallbacksOnce, [this]() { + if (!delayContextDestruction) { + std::atexit(context_at_exit_destructor); + } + // Get the function pointer for zelRegisterTeardownCallback from the loader + typedef ze_result_t (ZE_APICALL *zelRegisterTeardownCallback_t)( + zel_loader_teardown_callback_t, + zel_application_teardown_callback_t*, + uint32_t*); + auto pfnZelRegisterTeardownCallback = reinterpret_cast( + GET_FUNCTION_PTR(loader, "zelRegisterTeardownCallback")); + pfnZelRegisterTeardownCallback(staticLoaderTeardownCallback, &loaderTeardownCallback, &loaderTeardownCallbackIndex); + }); #endif return result; } @@ -393,6 +426,13 @@ zelSetDriverTeardown() { ze_result_t result = ZE_RESULT_SUCCESS; if (!ze_lib::destruction) { + if (ze_lib::context) { + // Call the teardown callbacks + for (auto &callback : ze_lib::context->teardownCallbacks) { + callback.second(); + } + } + ze_lib::destruction = true; } return result; @@ -472,6 +512,30 @@ void stabilityCheck(std::promise stabilityPromise) { } #endif +ZE_DLLEXPORT ze_result_t ZE_APICALL +zelRegisterTeardownCallback( + zel_loader_teardown_callback_t application_callback, // [in] Pointer to the user's application callback function + zel_application_teardown_callback_t *loader_callback, // [out] Pointer to the L0 Loader's callback function + uint32_t *index // [out] Index of the callback function +) { + ze_result_t result = ZE_RESULT_SUCCESS; + if (nullptr == application_callback) { + return ZE_RESULT_ERROR_INVALID_ARGUMENT; + } + if (!ze_lib::context) { + return ZE_RESULT_ERROR_UNINITIALIZED; + } + *loader_callback = ze_lib::applicationTeardownCallback; + ze_lib::context->teardownCallbacksCount.fetch_add(1); + *index = ze_lib::context->teardownCallbacksCount.load(); + ze_lib::context->teardownCallbacks.insert(std::pair(*index, application_callback)); + if (ze_lib::context->debugTraceEnabled) { + std::string message = "Registered teardown callback with index: " + std::to_string(*index); + ze_lib::context->debug_trace_message(message, ""); + } + return result; +} + /** * @brief Checks if the loader is in the process of tearing down. * @@ -493,6 +557,9 @@ zelCheckIsLoaderInTearDown() { return true; } #if defined(DYNAMIC_LOAD_LOADER) && defined(_WIN32) + if (ze_lib::loaderTeardownCallbackReceived) { + return true; + } std::promise stabilityPromise; std::future resultFuture = stabilityPromise.get_future(); int result = -1; diff --git a/source/lib/ze_lib.h b/source/lib/ze_lib.h index f4a2bad8..d026f47e 100644 --- a/source/lib/ze_lib.h +++ b/source/lib/ze_lib.h @@ -24,6 +24,9 @@ #include #include +typedef void (*zel_loader_teardown_callback_t)(); +typedef void (*zel_application_teardown_callback_t)(uint32_t index); + namespace ze_lib { /////////////////////////////////////////////////////////////////////////////// @@ -175,12 +178,20 @@ namespace ze_lib bool debugTraceEnabled = false; bool dynamicTracingSupported = true; ze_pfnDriverGet_t loaderDriverGet = nullptr; + std::atomic teardownCallbacksCount{0}; + std::map teardownCallbacks; + #ifdef DYNAMIC_LOAD_LOADER + std::once_flag initTeardownCallbacksOnce; + zel_application_teardown_callback_t loaderTeardownCallback = nullptr; + uint32_t loaderTeardownCallbackIndex = 0; + #endif }; extern bool destruction; extern context_t *context; #ifdef DYNAMIC_LOAD_LOADER extern bool delayContextDestruction; + extern bool loaderTeardownCallbackReceived; #endif } // namespace ze_lib From 9e14b6fd7f3953b272ea25d9eb31a2f82aa9ec25 Mon Sep 17 00:00:00 2001 From: "Neil R. Spruit" Date: Wed, 14 May 2025 17:33:22 -0700 Subject: [PATCH 02/10] Check that the loader has the symbol before attempting the register callback Signed-off-by: Neil R. Spruit --- source/lib/ze_lib.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/lib/ze_lib.cpp b/source/lib/ze_lib.cpp index 82f778e9..d8471439 100644 --- a/source/lib/ze_lib.cpp +++ b/source/lib/ze_lib.cpp @@ -373,7 +373,9 @@ namespace ze_lib uint32_t*); auto pfnZelRegisterTeardownCallback = reinterpret_cast( GET_FUNCTION_PTR(loader, "zelRegisterTeardownCallback")); - pfnZelRegisterTeardownCallback(staticLoaderTeardownCallback, &loaderTeardownCallback, &loaderTeardownCallbackIndex); + if (pfnZelRegisterTeardownCallback != nullptr) { + pfnZelRegisterTeardownCallback(staticLoaderTeardownCallback, &loaderTeardownCallback, &loaderTeardownCallbackIndex); + } }); #endif return result; From 513650a5b994b3a6fd886a17621901735d17d844 Mon Sep 17 00:00:00 2001 From: "Neil R. Spruit" Date: Wed, 14 May 2025 21:22:15 -0700 Subject: [PATCH 03/10] Cleanup documentation and comments Signed-off-by: Neil R. Spruit --- include/loader/ze_loader.h | 14 ++++++++------ source/lib/ze_lib.cpp | 17 ++++++----------- source/lib/ze_lib.h | 4 +--- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/include/loader/ze_loader.h b/include/loader/ze_loader.h index 7c49045a..7fa1b75b 100644 --- a/include/loader/ze_loader.h +++ b/include/loader/ze_loader.h @@ -95,17 +95,19 @@ ZE_DLLEXPORT bool ZE_APICALL zelCheckIsLoaderInTearDown(); /////////////////////////////////////////////////////////////////////////////// -/// @brief Exported function for registering a callback to indicate teardown. -/// @details The callback function will be invoked when the loader is in teardown. -/// +/// @brief Exported function for registering a teardown callback. +/// @details The provided application callback will be invoked when the loader enters teardown. +/// The loader's callback and the index for this application's callback reference are returned for later reference. +/// The loader's callback will be invoked when the application is torndown to inform the loader that the application +/// is being torn down and cannot be called to inform the loader of its teardown. typedef void (*zel_loader_teardown_callback_t)(); typedef void (*zel_application_teardown_callback_t)(uint32_t index); ZE_DLLEXPORT ze_result_t ZE_APICALL zelRegisterTeardownCallback( - zel_loader_teardown_callback_t application_callback, // [in] Pointer to the user's application callback function - zel_application_teardown_callback_t *loader_callback, // [out] Pointer to the L0 Loader's callback function - uint32_t *index // [out] Index of the callback function + zel_loader_teardown_callback_t application_callback, // [in] Application's callback function to be called during loader teardown + zel_application_teardown_callback_t *loader_callback, // [out] Pointer to the loader's callback function + uint32_t *index // [out] Index assigned to the registered callback ); /////////////////////////////////////////////////////////////////////////////// diff --git a/source/lib/ze_lib.cpp b/source/lib/ze_lib.cpp index d8471439..e2da3783 100644 --- a/source/lib/ze_lib.cpp +++ b/source/lib/ze_lib.cpp @@ -29,12 +29,10 @@ namespace ze_lib bool delayContextDestruction = false; bool loaderTeardownCallbackReceived = false; void staticLoaderTeardownCallback() { - printf("ze_lib Static Teardown Callback\n"); loaderTeardownCallbackReceived = true; } #endif void applicationTeardownCallback(uint32_t index) { - printf("ze_lib Application Teardown Callback %d\n", index); if (ze_lib::context->teardownCallbacks.find(index) != ze_lib::context->teardownCallbacks.end()) { ze_lib::context->teardownCallbacks.erase(index); } @@ -50,9 +48,6 @@ namespace ze_lib /////////////////////////////////////////////////////////////////////////////// __zedlllocal context_t::~context_t() { - if (debugTraceEnabled) { - debug_trace_message("ze_lib Context Destructor", ""); - } #ifdef DYNAMIC_LOAD_LOADER if (!loaderTeardownCallbackReceived) { loaderTeardownCallback(loaderTeardownCallbackIndex); @@ -368,8 +363,8 @@ namespace ze_lib } // Get the function pointer for zelRegisterTeardownCallback from the loader typedef ze_result_t (ZE_APICALL *zelRegisterTeardownCallback_t)( - zel_loader_teardown_callback_t, - zel_application_teardown_callback_t*, + zel_loader_teardown_callback_t, + zel_application_teardown_callback_t*, uint32_t*); auto pfnZelRegisterTeardownCallback = reinterpret_cast( GET_FUNCTION_PTR(loader, "zelRegisterTeardownCallback")); @@ -514,11 +509,11 @@ void stabilityCheck(std::promise stabilityPromise) { } #endif -ZE_DLLEXPORT ze_result_t ZE_APICALL +ze_result_t ZE_APICALL zelRegisterTeardownCallback( - zel_loader_teardown_callback_t application_callback, // [in] Pointer to the user's application callback function - zel_application_teardown_callback_t *loader_callback, // [out] Pointer to the L0 Loader's callback function - uint32_t *index // [out] Index of the callback function + zel_loader_teardown_callback_t application_callback, // [in] Application's callback function to be called during loader teardown + zel_application_teardown_callback_t *loader_callback, // [out] Pointer to the loader's callback function + uint32_t *index // [out] Index assigned to the registered callback ) { ze_result_t result = ZE_RESULT_SUCCESS; if (nullptr == application_callback) { diff --git a/source/lib/ze_lib.h b/source/lib/ze_lib.h index d026f47e..37f9d409 100644 --- a/source/lib/ze_lib.h +++ b/source/lib/ze_lib.h @@ -17,6 +17,7 @@ #include "layers/zel_tracing_api.h" #include "layers/zel_tracing_ddi.h" #include "../utils/logging.h" +#include "loader/ze_loader.h" #include "ze_util.h" #include #include @@ -24,9 +25,6 @@ #include #include -typedef void (*zel_loader_teardown_callback_t)(); -typedef void (*zel_application_teardown_callback_t)(uint32_t index); - namespace ze_lib { /////////////////////////////////////////////////////////////////////////////// From e867bbfc9a6b09dfe9a65769ec0eb45424fdd0c8 Mon Sep 17 00:00:00 2001 From: "Neil R. Spruit" Date: Thu, 15 May 2025 09:59:51 -0700 Subject: [PATCH 04/10] Only fallback to teardown thread if the registration is not available Signed-off-by: Neil R. Spruit --- source/lib/ze_lib.cpp | 50 +++++++++++++++++++++++-------------------- source/lib/ze_lib.h | 1 + 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/source/lib/ze_lib.cpp b/source/lib/ze_lib.cpp index e2da3783..f84db7c3 100644 --- a/source/lib/ze_lib.cpp +++ b/source/lib/ze_lib.cpp @@ -28,6 +28,7 @@ namespace ze_lib } bool delayContextDestruction = false; bool loaderTeardownCallbackReceived = false; + bool loaderTeardownRegistrationEnabled = false; void staticLoaderTeardownCallback() { loaderTeardownCallbackReceived = true; } @@ -370,6 +371,7 @@ namespace ze_lib GET_FUNCTION_PTR(loader, "zelRegisterTeardownCallback")); if (pfnZelRegisterTeardownCallback != nullptr) { pfnZelRegisterTeardownCallback(staticLoaderTeardownCallback, &loaderTeardownCallback, &loaderTeardownCallbackIndex); + loaderTeardownRegistrationEnabled = true; } }); #endif @@ -557,31 +559,33 @@ zelCheckIsLoaderInTearDown() { if (ze_lib::loaderTeardownCallbackReceived) { return true; } - std::promise stabilityPromise; - std::future resultFuture = stabilityPromise.get_future(); - int result = -1; - try { - // Launch the stability checker thread - std::thread stabilityThread(stabilityCheck, std::move(stabilityPromise)); - result = resultFuture.get(); // Blocks until the result is available - stabilityThread.join(); - } catch (const std::exception& e) { - if (ze_lib::context->debugTraceEnabled) { - std::string message = "Exception caught in parent thread: " + std::string(e.what()); - ze_lib::context->debug_trace_message(message, ""); - } - } catch (...) { - if (ze_lib::context->debugTraceEnabled) { - std::string message = "Unknown exception caught in parent thread."; - ze_lib::context->debug_trace_message(message, ""); + if (!ze_lib::loaderTeardownRegistrationEnabled) { + std::promise stabilityPromise; + std::future resultFuture = stabilityPromise.get_future(); + int result = -1; + try { + // Launch the stability checker thread + std::thread stabilityThread(stabilityCheck, std::move(stabilityPromise)); + result = resultFuture.get(); // Blocks until the result is available + stabilityThread.join(); + } catch (const std::exception& e) { + if (ze_lib::context->debugTraceEnabled) { + std::string message = "Exception caught in parent thread: " + std::string(e.what()); + ze_lib::context->debug_trace_message(message, ""); + } + } catch (...) { + if (ze_lib::context->debugTraceEnabled) { + std::string message = "Unknown exception caught in parent thread."; + ze_lib::context->debug_trace_message(message, ""); + } } - } - if (result != ZEL_STABILITY_CHECK_RESULT_SUCCESS) { - if (ze_lib::context->debugTraceEnabled) { - std::string message = "Loader stability check failed with result: " + std::to_string(result); - ze_lib::context->debug_trace_message(message, ""); + if (result != ZEL_STABILITY_CHECK_RESULT_SUCCESS) { + if (ze_lib::context->debugTraceEnabled) { + std::string message = "Loader stability check failed with result: " + std::to_string(result); + ze_lib::context->debug_trace_message(message, ""); + } + return true; } - return true; } #endif return false; diff --git a/source/lib/ze_lib.h b/source/lib/ze_lib.h index 37f9d409..883c2d5d 100644 --- a/source/lib/ze_lib.h +++ b/source/lib/ze_lib.h @@ -190,6 +190,7 @@ namespace ze_lib #ifdef DYNAMIC_LOAD_LOADER extern bool delayContextDestruction; extern bool loaderTeardownCallbackReceived; + extern bool loaderTeardownRegistrationEnabled; #endif } // namespace ze_lib From 3c7df271938319dc8308a14272e0dea8b92aec19 Mon Sep 17 00:00:00 2001 From: "Neil R. Spruit" Date: Thu, 15 May 2025 12:59:24 -0700 Subject: [PATCH 05/10] Add documentation and clear the callbacks given driver teardown Signed-off-by: Neil R. Spruit --- include/loader/ze_loader.h | 21 ++++++++++---- source/lib/ze_lib.cpp | 59 ++++++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/include/loader/ze_loader.h b/include/loader/ze_loader.h index 7fa1b75b..2f97cfad 100644 --- a/include/loader/ze_loader.h +++ b/include/loader/ze_loader.h @@ -94,15 +94,24 @@ zelEnableTracingLayer(); ZE_DLLEXPORT bool ZE_APICALL zelCheckIsLoaderInTearDown(); -/////////////////////////////////////////////////////////////////////////////// -/// @brief Exported function for registering a teardown callback. -/// @details The provided application callback will be invoked when the loader enters teardown. -/// The loader's callback and the index for this application's callback reference are returned for later reference. -/// The loader's callback will be invoked when the application is torndown to inform the loader that the application -/// is being torn down and cannot be called to inform the loader of its teardown. typedef void (*zel_loader_teardown_callback_t)(); typedef void (*zel_application_teardown_callback_t)(uint32_t index); +/** + * @brief Registers a teardown callback to be invoked during loader teardown. + * + * This function allows the application to register a callback function that will be called + * when the loader is being torn down. The loader will also provide its own callback function + * and assign an index to the registered callback. + * + * @param[in] application_callback Application's callback function to be called during loader teardown. + * @param[out] loader_callback Pointer to the loader's callback function. + * @param[out] index Index assigned to the registered callback. + * + * @return + * - ZE_RESULT_SUCCESS if the callback was successfully registered. + * - Appropriate error code otherwise. + */ ZE_DLLEXPORT ze_result_t ZE_APICALL zelRegisterTeardownCallback( zel_loader_teardown_callback_t application_callback, // [in] Application's callback function to be called during loader teardown diff --git a/source/lib/ze_lib.cpp b/source/lib/ze_lib.cpp index f84db7c3..551ee789 100644 --- a/source/lib/ze_lib.cpp +++ b/source/lib/ze_lib.cpp @@ -29,10 +29,25 @@ namespace ze_lib bool delayContextDestruction = false; bool loaderTeardownCallbackReceived = false; bool loaderTeardownRegistrationEnabled = false; + + /// @brief Callback function to handle loader teardown events. + /// + /// This function sets the `loaderTeardownCallbackReceived` flag to true, + /// indicating that a loader teardown callback has been received. + /// It is intended to be used as a static callback during the loader's + /// teardown process. void staticLoaderTeardownCallback() { loaderTeardownCallbackReceived = true; } #endif + /** + * @brief Removes a teardown callback from the context's callback registry. + * + * This function checks if a teardown callback with the specified index exists + * in the context's teardownCallbacks map. If it exists, the callback is removed. + * + * @param index The unique identifier of the teardown callback to remove. + */ void applicationTeardownCallback(uint32_t index) { if (ze_lib::context->teardownCallbacks.find(index) != ze_lib::context->teardownCallbacks.end()) { ze_lib::context->teardownCallbacks.erase(index); @@ -57,10 +72,11 @@ namespace ze_lib FREE_DRIVER_LIBRARY( loader ); } #else - // Call the teardown callbacks + // Given the loader teardown, notify the registered callbacks that the loader is being torn down. for (auto &callback : teardownCallbacks) { callback.second(); } + // Clear the teardown callbacks map once the callbacks have been executed. teardownCallbacks.clear(); #endif ze_lib::destruction = true; @@ -362,7 +378,7 @@ namespace ze_lib if (!delayContextDestruction) { std::atexit(context_at_exit_destructor); } - // Get the function pointer for zelRegisterTeardownCallback from the loader + // Get the function pointer for zelRegisterTeardownCallback from the dynamic loader typedef ze_result_t (ZE_APICALL *zelRegisterTeardownCallback_t)( zel_loader_teardown_callback_t, zel_application_teardown_callback_t*, @@ -370,8 +386,15 @@ namespace ze_lib auto pfnZelRegisterTeardownCallback = reinterpret_cast( GET_FUNCTION_PTR(loader, "zelRegisterTeardownCallback")); if (pfnZelRegisterTeardownCallback != nullptr) { - pfnZelRegisterTeardownCallback(staticLoaderTeardownCallback, &loaderTeardownCallback, &loaderTeardownCallbackIndex); - loaderTeardownRegistrationEnabled = true; + auto register_teardown_result = pfnZelRegisterTeardownCallback(staticLoaderTeardownCallback, &loaderTeardownCallback, &loaderTeardownCallbackIndex); + if (register_teardown_result != ZE_RESULT_SUCCESS) { + std::string message = "ze_lib Context Init() zelRegisterTeardownCallback failed with "; + debug_trace_message(message, to_string(register_teardown_result)); + } else { + loaderTeardownRegistrationEnabled = true; + std::string message = "ze_lib Context Init() zelRegisterTeardownCallback completed for the static loader with"; + debug_trace_message(message, to_string(register_teardown_result)); + } } }); #endif @@ -426,10 +449,12 @@ zelSetDriverTeardown() ze_result_t result = ZE_RESULT_SUCCESS; if (!ze_lib::destruction) { if (ze_lib::context) { - // Call the teardown callbacks + // Given the driver teardown, notify the registered callbacks that the loader is being torn down. for (auto &callback : ze_lib::context->teardownCallbacks) { callback.second(); } + // Clear the registered callbacks now that they have been called. + ze_lib::context->teardownCallbacks.clear(); } ze_lib::destruction = true; @@ -511,6 +536,23 @@ void stabilityCheck(std::promise stabilityPromise) { } #endif +/// @brief Registers a teardown callback function to be invoked during loader teardown. +/// +/// This function allows an application to register a callback that will be called when the loader is being torn down. +/// The loader provides a callback function pointer to the application, which the application should call to notify +/// the loader that it is tearing down. The loader will then remove the application's callback from its list of registered callbacks. +/// +/// @param[in] application_callback +/// The application's callback function to be called during loader teardown. Must not be nullptr. +/// @param[out] loader_callback +/// Pointer to the loader's callback function. The application should call this function to notify the loader of teardown. +/// @param[out] index +/// Pointer to a uint32_t that will receive the index assigned to the registered callback. +/// +/// @return +/// - ZE_RESULT_SUCCESS: The callback was successfully registered. +/// - ZE_RESULT_ERROR_INVALID_ARGUMENT: The application_callback parameter is nullptr. +/// - ZE_RESULT_ERROR_UNINITIALIZED: The loader context is not initialized. ze_result_t ZE_APICALL zelRegisterTeardownCallback( zel_loader_teardown_callback_t application_callback, // [in] Application's callback function to be called during loader teardown @@ -524,7 +566,10 @@ zelRegisterTeardownCallback( if (!ze_lib::context) { return ZE_RESULT_ERROR_UNINITIALIZED; } + // Assign the loader's callback function to the application callback such that the application can notify the loader + // that it is tearing down. The loader will then remove the application's callback from the list of callbacks. *loader_callback = ze_lib::applicationTeardownCallback; + // Increment the teardown callback count and assign the index to the application callback. ze_lib::context->teardownCallbacksCount.fetch_add(1); *index = ze_lib::context->teardownCallbacksCount.load(); ze_lib::context->teardownCallbacks.insert(std::pair(*index, application_callback)); @@ -557,6 +602,10 @@ zelCheckIsLoaderInTearDown() { } #if defined(DYNAMIC_LOAD_LOADER) && defined(_WIN32) if (ze_lib::loaderTeardownCallbackReceived) { + if (ze_lib::context->debugTraceEnabled) { + std::string message = "Loader Teardown Notification Received, loader in teardown state."; + ze_lib::context->debug_trace_message(message, ""); + } return true; } if (!ze_lib::loaderTeardownRegistrationEnabled) { From a4fa9b96d2831dd37c13ee8386cdae98b9ba1cac Mon Sep 17 00:00:00 2001 From: "Neil R. Spruit" Date: Thu, 15 May 2025 13:13:50 -0700 Subject: [PATCH 06/10] Add documentation for when application teardown is called Signed-off-by: Neil R. Spruit --- source/lib/ze_lib.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/lib/ze_lib.cpp b/source/lib/ze_lib.cpp index 551ee789..c34f4a5f 100644 --- a/source/lib/ze_lib.cpp +++ b/source/lib/ze_lib.cpp @@ -50,6 +50,10 @@ namespace ze_lib */ void applicationTeardownCallback(uint32_t index) { if (ze_lib::context->teardownCallbacks.find(index) != ze_lib::context->teardownCallbacks.end()) { + if (ze_lib::context->debugTraceEnabled) { + std::string message = "applicationTeardownCallback received for index: " + std::to_string(index); + ze_lib::context->debug_trace_message(message, ""); + } ze_lib::context->teardownCallbacks.erase(index); } } From dbdd143a024e515e670d8be8a96fe0fc3e7fc7bc Mon Sep 17 00:00:00 2001 From: "Neil R. Spruit" Date: Thu, 15 May 2025 14:39:06 -0700 Subject: [PATCH 07/10] Simplify the fallback teardown check and check only once before failing. Signed-off-by: Neil R. Spruit --- source/lib/ze_lib.cpp | 114 ++++++++++++------------------------------ 1 file changed, 31 insertions(+), 83 deletions(-) diff --git a/source/lib/ze_lib.cpp b/source/lib/ze_lib.cpp index c34f4a5f..fde9de08 100644 --- a/source/lib/ze_lib.cpp +++ b/source/lib/ze_lib.cpp @@ -69,7 +69,7 @@ namespace ze_lib __zedlllocal context_t::~context_t() { #ifdef DYNAMIC_LOAD_LOADER - if (!loaderTeardownCallbackReceived) { + if (loaderTeardownRegistrationEnabled && !loaderTeardownCallbackReceived) { loaderTeardownCallback(loaderTeardownCallbackIndex); } if (loader) { @@ -476,70 +476,6 @@ zelSetDelayLoaderContextTeardown() #endif } -#ifdef DYNAMIC_LOAD_LOADER -#define ZEL_STABILITY_CHECK_RESULT_SUCCESS 0 -#define ZEL_STABILITY_CHECK_RESULT_DRIVER_GET_NULL 1 -#define ZEL_STABILITY_CHECK_RESULT_DRIVER_GET_FAILED 2 -#define ZEL_STABILITY_CHECK_RESULT_EXCEPTION 3 - -/** - * @brief Performs a stability check for the Level Zero loader. - * - * This function checks the stability of the Level Zero loader by verifying - * the presence of the loader module, the validity of the `zeDriverGet` function - * pointer, and the ability to retrieve driver information. The result of the - * stability check is communicated through the provided promise. - * - * @param stabilityPromise A promise object used to communicate the result of - * the stability check. The promise is set with one of - * the following values: - * - ZEL_STABILITY_CHECK_RESULT_DRIVER_GET_NULL: The - * `zeDriverGet` function pointer is invalid. - * - ZEL_STABILITY_CHECK_RESULT_DRIVER_GET_FAILED: The - * loader failed to retrieve driver information. - * - ZEL_STABILITY_CHECK_RESULT_EXCEPTION: An - * exception occurred during the stability check. - * - ZEL_STABILITY_CHECK_RESULT_SUCCESS: The stability - * check was successful. - * - * @note If debug tracing is enabled, debug messages are logged for each failure - * scenario. - * @note If the Loader is completely torn down, this thread is expected to be killed - * due to invalid memory access and the stability check will determine a failure. - * - * @exception This function catches all exceptions internally and does not throw. - */ -void stabilityCheck(std::promise stabilityPromise) { - try { - if (!ze_lib::context->loaderDriverGet) { - if (ze_lib::context->debugTraceEnabled) { - std::string message = "LoaderDriverGet is a bad pointer. Exiting stability checker thread."; - ze_lib::context->debug_trace_message(message, ""); - } - stabilityPromise.set_value(ZEL_STABILITY_CHECK_RESULT_DRIVER_GET_NULL); - return; - } - - uint32_t driverCount = 0; - ze_result_t result = ZE_RESULT_ERROR_UNINITIALIZED; - result = ze_lib::context->loaderDriverGet(&driverCount, nullptr); - if (result != ZE_RESULT_SUCCESS || driverCount == 0) { - if (ze_lib::context->debugTraceEnabled) { - std::string message = "Loader stability check failed. Exiting stability checker thread."; - ze_lib::context->debug_trace_message(message, ""); - } - stabilityPromise.set_value(ZEL_STABILITY_CHECK_RESULT_DRIVER_GET_FAILED); - return; - } - stabilityPromise.set_value(ZEL_STABILITY_CHECK_RESULT_SUCCESS); - return; - } catch (...) { - stabilityPromise.set_value(ZEL_STABILITY_CHECK_RESULT_EXCEPTION); - return; - } -} -#endif - /// @brief Registers a teardown callback function to be invoked during loader teardown. /// /// This function allows an application to register a callback that will be called when the loader is being torn down. @@ -605,38 +541,50 @@ zelCheckIsLoaderInTearDown() { return true; } #if defined(DYNAMIC_LOAD_LOADER) && defined(_WIN32) + static bool loaderIsStable = true; + if (!loaderIsStable) { + if (ze_lib::context->debugTraceEnabled) { + std::string message = "Loader Teardown check failed before, exiting."; + ze_lib::context->debug_trace_message(message, ""); + } + return true; + } if (ze_lib::loaderTeardownCallbackReceived) { if (ze_lib::context->debugTraceEnabled) { std::string message = "Loader Teardown Notification Received, loader in teardown state."; ze_lib::context->debug_trace_message(message, ""); } + loaderIsStable = false; return true; } if (!ze_lib::loaderTeardownRegistrationEnabled) { - std::promise stabilityPromise; - std::future resultFuture = stabilityPromise.get_future(); - int result = -1; try { - // Launch the stability checker thread - std::thread stabilityThread(stabilityCheck, std::move(stabilityPromise)); - result = resultFuture.get(); // Blocks until the result is available - stabilityThread.join(); - } catch (const std::exception& e) { - if (ze_lib::context->debugTraceEnabled) { - std::string message = "Exception caught in parent thread: " + std::string(e.what()); - ze_lib::context->debug_trace_message(message, ""); + if (!ze_lib::context->loaderDriverGet) { + if (ze_lib::context->debugTraceEnabled) { + std::string message = "LoaderDriverGet is a bad pointer. Exiting stability checker."; + ze_lib::context->debug_trace_message(message, ""); + } + loaderIsStable = false; + return true; } - } catch (...) { - if (ze_lib::context->debugTraceEnabled) { - std::string message = "Unknown exception caught in parent thread."; - ze_lib::context->debug_trace_message(message, ""); + + uint32_t driverCount = 0; + ze_result_t result = ZE_RESULT_ERROR_UNINITIALIZED; + result = ze_lib::context->loaderDriverGet(&driverCount, nullptr); + if (result != ZE_RESULT_SUCCESS || driverCount == 0) { + if (ze_lib::context->debugTraceEnabled) { + std::string message = "Loader stability check failed. Exiting stability checker."; + ze_lib::context->debug_trace_message(message, ""); + } + loaderIsStable = false; + return true; } - } - if (result != ZEL_STABILITY_CHECK_RESULT_SUCCESS) { + } catch (...) { if (ze_lib::context->debugTraceEnabled) { - std::string message = "Loader stability check failed with result: " + std::to_string(result); + std::string message = "Loader stability check failed. Exception occurred."; ze_lib::context->debug_trace_message(message, ""); } + loaderIsStable = false; return true; } } From 2b9b5f1e332043605e8208df0e13686723a18eef Mon Sep 17 00:00:00 2001 From: "Neil R. Spruit" Date: Thu, 15 May 2025 14:57:55 -0700 Subject: [PATCH 08/10] Fix documentation for zelCheckIsLoaderInTearDown Signed-off-by: Neil R. Spruit --- source/lib/ze_lib.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/source/lib/ze_lib.cpp b/source/lib/ze_lib.cpp index fde9de08..f3e920c3 100644 --- a/source/lib/ze_lib.cpp +++ b/source/lib/ze_lib.cpp @@ -520,21 +520,16 @@ zelRegisterTeardownCallback( return result; } -/** - * @brief Checks if the loader is in the process of tearing down. - * - * This function determines whether the loader is in a teardown state by - * checking the destruction flag or the context pointer. If the loader is - * dynamically loaded thru the static loader code path, then it performs - * an additional stability check using a separate thread that could be killed. - * - * @return true if the loader is in teardown based on the stack variablrs - * or the stability check fails; false otherwise. - * - * @note If the macro DYNAMIC_LOAD_LOADER is defined, a stability checker - * thread is launched to perform additional checks. Any exceptions - * or errors during this process are logged if debug tracing is enabled. - */ +/// @brief Checks if the Level Zero loader is currently in the teardown state. +/// +/// This function determines whether the loader is in the process of being destroyed or is otherwise +/// unavailable for further API calls. It performs several checks, including: +/// - Whether the loader's destruction flag is set or the context is null. +/// - On Windows with dynamic loading, it checks for loader teardown notifications, +/// registration status, and the stability of the loader by attempting to call `loaderDriverGet`. +/// - If any of these checks indicate the loader is in teardown or unstable, the function returns true. +/// +/// @return true if the loader is in teardown or unstable; false otherwise. bool ZE_APICALL zelCheckIsLoaderInTearDown() { if (ze_lib::destruction || ze_lib::context == nullptr) { From 6cecaa346cd734c87b79efa5b5e9deb63544d2c1 Mon Sep 17 00:00:00 2001 From: "Neil R. Spruit" Date: Thu, 15 May 2025 15:36:14 -0700 Subject: [PATCH 09/10] Add lock guard around teardown callbacks map Signed-off-by: Neil R. Spruit --- source/lib/ze_lib.cpp | 24 ++++++++++++++---------- source/lib/ze_lib.h | 1 + 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/source/lib/ze_lib.cpp b/source/lib/ze_lib.cpp index f3e920c3..b4554d52 100644 --- a/source/lib/ze_lib.cpp +++ b/source/lib/ze_lib.cpp @@ -49,6 +49,7 @@ namespace ze_lib * @param index The unique identifier of the teardown callback to remove. */ void applicationTeardownCallback(uint32_t index) { + std::lock_guard lock(ze_lib::context->teardownCallbacksMutex); if (ze_lib::context->teardownCallbacks.find(index) != ze_lib::context->teardownCallbacks.end()) { if (ze_lib::context->debugTraceEnabled) { std::string message = "applicationTeardownCallback received for index: " + std::to_string(index); @@ -506,16 +507,19 @@ zelRegisterTeardownCallback( if (!ze_lib::context) { return ZE_RESULT_ERROR_UNINITIALIZED; } - // Assign the loader's callback function to the application callback such that the application can notify the loader - // that it is tearing down. The loader will then remove the application's callback from the list of callbacks. - *loader_callback = ze_lib::applicationTeardownCallback; - // Increment the teardown callback count and assign the index to the application callback. - ze_lib::context->teardownCallbacksCount.fetch_add(1); - *index = ze_lib::context->teardownCallbacksCount.load(); - ze_lib::context->teardownCallbacks.insert(std::pair(*index, application_callback)); - if (ze_lib::context->debugTraceEnabled) { - std::string message = "Registered teardown callback with index: " + std::to_string(*index); - ze_lib::context->debug_trace_message(message, ""); + { + std::lock_guard lock(ze_lib::context->teardownCallbacksMutex); + // Assign the loader's callback function to the application callback such that the application can notify the loader + // that it is tearing down. The loader will then remove the application's callback from the list of callbacks. + *loader_callback = ze_lib::applicationTeardownCallback; + // Increment the teardown callback count and assign the index to the application callback. + ze_lib::context->teardownCallbacksCount.fetch_add(1); + *index = ze_lib::context->teardownCallbacksCount.load(); + ze_lib::context->teardownCallbacks.insert(std::pair(*index, application_callback)); + if (ze_lib::context->debugTraceEnabled) { + std::string message = "Registered teardown callback with index: " + std::to_string(*index); + ze_lib::context->debug_trace_message(message, ""); + } } return result; } diff --git a/source/lib/ze_lib.h b/source/lib/ze_lib.h index 883c2d5d..4e829f1f 100644 --- a/source/lib/ze_lib.h +++ b/source/lib/ze_lib.h @@ -178,6 +178,7 @@ namespace ze_lib ze_pfnDriverGet_t loaderDriverGet = nullptr; std::atomic teardownCallbacksCount{0}; std::map teardownCallbacks; + std::mutex teardownCallbacksMutex; #ifdef DYNAMIC_LOAD_LOADER std::once_flag initTeardownCallbacksOnce; zel_application_teardown_callback_t loaderTeardownCallback = nullptr; From 6adcb00d6b1dabe093901f11add59b90ff05a7c8 Mon Sep 17 00:00:00 2001 From: "Neil R. Spruit" Date: Fri, 16 May 2025 12:58:35 -0700 Subject: [PATCH 10/10] Add documentation on callback requirements Signed-off-by: Neil R. Spruit --- include/loader/ze_loader.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/loader/ze_loader.h b/include/loader/ze_loader.h index 2f97cfad..5b6261e2 100644 --- a/include/loader/ze_loader.h +++ b/include/loader/ze_loader.h @@ -104,6 +104,16 @@ typedef void (*zel_application_teardown_callback_t)(uint32_t index); * when the loader is being torn down. The loader will also provide its own callback function * and assign an index to the registered callback. * + * The application_callback is required to be a function that takes no arguments and returns void. + * In addition, the application_callback should be thread-safe and not block to prevent deadlocking the + * loader teardown process. + * + * For example, the application_callback used by the static loader is: + * void staticLoaderTeardownCallback() { + * loaderTeardownCallbackReceived = true; + * } + * The application_callback should provide a simple notification to the application that the loader is being torn down. + * * @param[in] application_callback Application's callback function to be called during loader teardown. * @param[out] loader_callback Pointer to the loader's callback function. * @param[out] index Index assigned to the registered callback.