From 0005bca5ed77d9cbad30646c47565485ecba9e57 Mon Sep 17 00:00:00 2001 From: Matthew Michel Date: Tue, 19 May 2026 16:04:45 -0700 Subject: [PATCH 1/2] [UR][L0v2] Add spec and implementation for urGraphSetDestructionCallbackExp --- .../include/unified-runtime/ur_api.h | 40 ++++++++++ .../include/unified-runtime/ur_api_funcs.def | 1 + .../include/unified-runtime/ur_ddi.h | 6 ++ .../include/unified-runtime/ur_print.h | 10 +++ .../include/unified-runtime/ur_print.hpp | 33 ++++++++ unified-runtime/scripts/core/EXP-GRAPH.rst | 5 ++ unified-runtime/scripts/core/exp-graph.yml | 24 ++++++ unified-runtime/scripts/core/registry.yml | 5 +- .../source/adapters/cuda/graph.cpp | 7 ++ .../adapters/cuda/ur_interface_loader.cpp | 1 + unified-runtime/source/adapters/hip/graph.cpp | 7 ++ .../adapters/hip/ur_interface_loader.cpp | 1 + .../source/adapters/level_zero/graph.cpp | 10 +++ .../source/adapters/level_zero/platform.cpp | 3 + .../source/adapters/level_zero/platform.hpp | 3 + .../level_zero/ur_interface_loader.cpp | 2 + .../level_zero/ur_interface_loader.hpp | 3 + .../source/adapters/level_zero/v2/graph.cpp | 45 +++++++++++ .../source/adapters/mock/ur_mockddi.cpp | 55 +++++++++++++ .../source/adapters/native_cpu/graph.cpp | 8 ++ .../native_cpu/ur_interface_loader.cpp | 1 + .../source/adapters/offload/graph.cpp | 7 ++ .../adapters/offload/ur_interface_loader.cpp | 1 + .../source/adapters/opencl/graph.cpp | 7 ++ .../adapters/opencl/ur_interface_loader.cpp | 1 + .../loader/layers/tracing/ur_trcddi.cpp | 50 ++++++++++++ .../loader/layers/validation/ur_valddi.cpp | 37 +++++++++ unified-runtime/source/loader/loader.def.in | 2 + unified-runtime/source/loader/loader.map.in | 2 + unified-runtime/source/loader/ur_ldrddi.cpp | 25 ++++++ unified-runtime/source/loader/ur_libapi.cpp | 31 ++++++++ unified-runtime/source/loader/ur_print.cpp | 8 ++ unified-runtime/source/ur_api.cpp | 25 ++++++ .../test/conformance/exp_graph/CMakeLists.txt | 1 + .../urGraphSetDestructionCallbackExp.cpp | 79 +++++++++++++++++++ 35 files changed, 545 insertions(+), 1 deletion(-) create mode 100644 unified-runtime/test/conformance/exp_graph/urGraphSetDestructionCallbackExp.cpp diff --git a/unified-runtime/include/unified-runtime/ur_api.h b/unified-runtime/include/unified-runtime/ur_api.h index ce889941d39c6..798423b6ae55a 100644 --- a/unified-runtime/include/unified-runtime/ur_api.h +++ b/unified-runtime/include/unified-runtime/ur_api.h @@ -510,6 +510,8 @@ typedef enum ur_function_t { UR_FUNCTION_USM_HOST_ALLOC_UNREGISTER_EXP = 313, /// Enumerator for ::urQueueGetGraphExp UR_FUNCTION_QUEUE_GET_GRAPH_EXP = 314, + /// Enumerator for ::urGraphSetDestructionCallbackExp + UR_FUNCTION_GRAPH_SET_DESTRUCTION_CALLBACK_EXP = 315, /// @cond UR_FUNCTION_FORCE_UINT32 = 0x7fffffff /// @endcond @@ -13874,6 +13876,34 @@ UR_APIEXPORT ur_result_t UR_APICALL urGraphIsEmptyExp( /// [out] Pointer to a boolean where the result will be stored. bool *pResult); +/////////////////////////////////////////////////////////////////////////////// +/// @brief Callback function invoked when a graph is destroyed. +typedef void (*ur_exp_graph_destruction_callback_t)( + /// [in] pointer to user data to be passed to the callback + void *pUserData); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Register a callback to be invoked when the graph is destroyed. +/// +/// @returns +/// - ::UR_RESULT_SUCCESS +/// - ::UR_RESULT_ERROR_UNINITIALIZED +/// - ::UR_RESULT_ERROR_DEVICE_LOST +/// - ::UR_RESULT_ERROR_ADAPTER_SPECIFIC +/// - ::UR_RESULT_ERROR_INVALID_NULL_HANDLE +/// + `NULL == hGraph` +/// - ::UR_RESULT_ERROR_INVALID_NULL_POINTER +/// + `NULL == pfnCallback` +UR_APIEXPORT ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + /// [in] Handle of the graph to register the callback for. + ur_exp_graph_handle_t hGraph, + /// [in] Function pointer to the callback. The callback must not access + /// hGraph. + ur_exp_graph_destruction_callback_t pfnCallback, + /// [in][optional] Pointer to user data to be passed to the callback. The + /// user data must not reference hGraph. + void *pUserData); + /////////////////////////////////////////////////////////////////////////////// /// @brief Dump the contents of the recorded graph to the provided file path. /// @@ -16414,6 +16444,16 @@ typedef struct ur_graph_is_empty_exp_params_t { bool **ppResult; } ur_graph_is_empty_exp_params_t; +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function parameters for urGraphSetDestructionCallbackExp +/// @details Each entry is a pointer to the parameter passed to the function; +/// allowing the callback the ability to modify the parameter's value +typedef struct ur_graph_set_destruction_callback_exp_params_t { + ur_exp_graph_handle_t *phGraph; + ur_exp_graph_destruction_callback_t *ppfnCallback; + void **ppUserData; +} ur_graph_set_destruction_callback_exp_params_t; + /////////////////////////////////////////////////////////////////////////////// /// @brief Function parameters for urGraphDumpContentsExp /// @details Each entry is a pointer to the parameter passed to the function; diff --git a/unified-runtime/include/unified-runtime/ur_api_funcs.def b/unified-runtime/include/unified-runtime/ur_api_funcs.def index c73c88213600f..fc481d947a8c7 100644 --- a/unified-runtime/include/unified-runtime/ur_api_funcs.def +++ b/unified-runtime/include/unified-runtime/ur_api_funcs.def @@ -217,6 +217,7 @@ _UR_API(urGraphInstantiateGraphExp) _UR_API(urGraphDestroyExp) _UR_API(urGraphExecutableGraphDestroyExp) _UR_API(urGraphIsEmptyExp) +_UR_API(urGraphSetDestructionCallbackExp) _UR_API(urGraphDumpContentsExp) _UR_API(urIPCGetMemHandleExp) _UR_API(urIPCPutMemHandleExp) diff --git a/unified-runtime/include/unified-runtime/ur_ddi.h b/unified-runtime/include/unified-runtime/ur_ddi.h index 2fda5130c0ed8..cc734e6194d92 100644 --- a/unified-runtime/include/unified-runtime/ur_ddi.h +++ b/unified-runtime/include/unified-runtime/ur_ddi.h @@ -1887,6 +1887,11 @@ typedef ur_result_t(UR_APICALL *ur_pfnGraphExecutableGraphDestroyExp_t)( typedef ur_result_t(UR_APICALL *ur_pfnGraphIsEmptyExp_t)(ur_exp_graph_handle_t, bool *); +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function-pointer for urGraphSetDestructionCallbackExp +typedef ur_result_t(UR_APICALL *ur_pfnGraphSetDestructionCallbackExp_t)( + ur_exp_graph_handle_t, ur_exp_graph_destruction_callback_t, void *); + /////////////////////////////////////////////////////////////////////////////// /// @brief Function-pointer for urGraphDumpContentsExp typedef ur_result_t(UR_APICALL *ur_pfnGraphDumpContentsExp_t)( @@ -1900,6 +1905,7 @@ typedef struct ur_graph_exp_dditable_t { ur_pfnGraphDestroyExp_t pfnDestroyExp; ur_pfnGraphExecutableGraphDestroyExp_t pfnExecutableGraphDestroyExp; ur_pfnGraphIsEmptyExp_t pfnIsEmptyExp; + ur_pfnGraphSetDestructionCallbackExp_t pfnSetDestructionCallbackExp; ur_pfnGraphDumpContentsExp_t pfnDumpContentsExp; } ur_graph_exp_dditable_t; diff --git a/unified-runtime/include/unified-runtime/ur_print.h b/unified-runtime/include/unified-runtime/ur_print.h index f309275767a0f..d9acae65c7d07 100644 --- a/unified-runtime/include/unified-runtime/ur_print.h +++ b/unified-runtime/include/unified-runtime/ur_print.h @@ -3725,6 +3725,16 @@ UR_APIEXPORT ur_result_t UR_APICALL urPrintGraphIsEmptyExpParams( const struct ur_graph_is_empty_exp_params_t *params, char *buffer, const size_t buff_size, size_t *out_size); +/////////////////////////////////////////////////////////////////////////////// +/// @brief Print ur_graph_set_destruction_callback_exp_params_t struct +/// @returns +/// - ::UR_RESULT_SUCCESS +/// - ::UR_RESULT_ERROR_INVALID_SIZE +/// - `buff_size < out_size` +UR_APIEXPORT ur_result_t UR_APICALL urPrintGraphSetDestructionCallbackExpParams( + const struct ur_graph_set_destruction_callback_exp_params_t *params, + char *buffer, const size_t buff_size, size_t *out_size); + /////////////////////////////////////////////////////////////////////////////// /// @brief Print ur_graph_dump_contents_exp_params_t struct /// @returns diff --git a/unified-runtime/include/unified-runtime/ur_print.hpp b/unified-runtime/include/unified-runtime/ur_print.hpp index fba0340dea515..c29373a7d162a 100644 --- a/unified-runtime/include/unified-runtime/ur_print.hpp +++ b/unified-runtime/include/unified-runtime/ur_print.hpp @@ -1377,6 +1377,9 @@ inline std::ostream &operator<<(std::ostream &os, enum ur_function_t value) { case UR_FUNCTION_QUEUE_GET_GRAPH_EXP: os << "UR_FUNCTION_QUEUE_GET_GRAPH_EXP"; break; + case UR_FUNCTION_GRAPH_SET_DESTRUCTION_CALLBACK_EXP: + os << "UR_FUNCTION_GRAPH_SET_DESTRUCTION_CALLBACK_EXP"; + break; default: os << "unknown enumerator"; break; @@ -21568,6 +21571,33 @@ inline std::ostream &operator<<( return os; } +/////////////////////////////////////////////////////////////////////////////// +/// @brief Print operator for the ur_graph_set_destruction_callback_exp_params_t +/// type +/// @returns +/// std::ostream & +inline std::ostream &operator<<( + std::ostream &os, + [[maybe_unused]] const struct ur_graph_set_destruction_callback_exp_params_t + *params) { + + os << ".hGraph = "; + + ur::details::printPtr(os, *(params->phGraph)); + + os << ", "; + os << ".pfnCallback = "; + + os << reinterpret_cast(*(params->ppfnCallback)); + + os << ", "; + os << ".pUserData = "; + + ur::details::printPtr(os, *(params->ppUserData)); + + return os; +} + /////////////////////////////////////////////////////////////////////////////// /// @brief Print operator for the ur_graph_dump_contents_exp_params_t type /// @returns @@ -23232,6 +23262,9 @@ inline ur_result_t UR_APICALL printFunctionParams(std::ostream &os, case UR_FUNCTION_GRAPH_IS_EMPTY_EXP: { os << (const struct ur_graph_is_empty_exp_params_t *)params; } break; + case UR_FUNCTION_GRAPH_SET_DESTRUCTION_CALLBACK_EXP: { + os << (const struct ur_graph_set_destruction_callback_exp_params_t *)params; + } break; case UR_FUNCTION_GRAPH_DUMP_CONTENTS_EXP: { os << (const struct ur_graph_dump_contents_exp_params_t *)params; } break; diff --git a/unified-runtime/scripts/core/EXP-GRAPH.rst b/unified-runtime/scripts/core/EXP-GRAPH.rst index 82f56d62781dc..970664f6bc3e1 100644 --- a/unified-runtime/scripts/core/EXP-GRAPH.rst +++ b/unified-runtime/scripts/core/EXP-GRAPH.rst @@ -72,6 +72,10 @@ Changelog | 1.3 | Add ${x}QueueGetGraphExp to retrieve graph | | | handle from queue in capture mode. | +-----------+---------------------------------------------+ +| 1.4 | Add ${x}GraphSetDestructionCallbackExp to | +| | register user callbacks invoked on graph | +| | destruction. | ++-----------+---------------------------------------------+ Support -------------------------------------------------------------------------------- @@ -84,3 +88,4 @@ Contributors * Krzysztof, Filipek `krzysztof.filipek@intel.com `_ * Krzysztof, Swiecicki `krzysztof.swiecicki@intel.com `_ +* Matthew, Michel `matthew.michel@intel.com `_ diff --git a/unified-runtime/scripts/core/exp-graph.yml b/unified-runtime/scripts/core/exp-graph.yml index c01465825b6c0..069cb0d0286c8 100644 --- a/unified-runtime/scripts/core/exp-graph.yml +++ b/unified-runtime/scripts/core/exp-graph.yml @@ -187,6 +187,30 @@ params: returns: - $X_RESULT_ERROR_INVALID_GRAPH --- #-------------------------------------------------------------------------- +type: fptr_typedef +desc: "Callback function invoked when a graph is destroyed." +name: $x_exp_graph_destruction_callback_t +return: void +params: + - type: void* + name: pUserData + desc: "[in] pointer to user data to be passed to the callback" +--- #-------------------------------------------------------------------------- +type: function +desc: "Register a callback to be invoked when the graph is destroyed." +class: $xGraph +name: SetDestructionCallbackExp +params: + - type: $x_exp_graph_handle_t + name: hGraph + desc: "[in] Handle of the graph to register the callback for." + - type: $x_exp_graph_destruction_callback_t + name: pfnCallback + desc: "[in] Function pointer to the callback. The callback must not access hGraph." + - type: void* + name: pUserData + desc: "[in][optional] Pointer to user data to be passed to the callback. The user data must not reference hGraph." +--- #-------------------------------------------------------------------------- type: function desc: "Dump the contents of the recorded graph to the provided file path." class: $xGraph diff --git a/unified-runtime/scripts/core/registry.yml b/unified-runtime/scripts/core/registry.yml index 6c039b80e41d9..f7a11b40c6547 100644 --- a/unified-runtime/scripts/core/registry.yml +++ b/unified-runtime/scripts/core/registry.yml @@ -724,7 +724,10 @@ etors: - name: QUEUE_GET_GRAPH_EXP desc: Enumerator for $xQueueGetGraphExp value: '314' -max_id: '314' +- name: GRAPH_SET_DESTRUCTION_CALLBACK_EXP + desc: Enumerator for $xGraphSetDestructionCallbackExp + value: '315' +max_id: '315' --- type: enum desc: Defines structure types diff --git a/unified-runtime/source/adapters/cuda/graph.cpp b/unified-runtime/source/adapters/cuda/graph.cpp index f7526b4d0a590..cbee57bf2dae1 100644 --- a/unified-runtime/source/adapters/cuda/graph.cpp +++ b/unified-runtime/source/adapters/cuda/graph.cpp @@ -29,6 +29,13 @@ urGraphIsEmptyExp(ur_exp_graph_handle_t /* hGraph */, bool * /* pIsEmpty */) { return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; } +UR_APIEXPORT ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + ur_exp_graph_handle_t /* hGraph */, + ur_exp_graph_destruction_callback_t /* pfnCallback */, + void * /* pUserData */) { + return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; +} + UR_APIEXPORT ur_result_t UR_APICALL urGraphDumpContentsExp( ur_exp_graph_handle_t /* hGraph */, const char * /* pDotFilePath */) { return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; diff --git a/unified-runtime/source/adapters/cuda/ur_interface_loader.cpp b/unified-runtime/source/adapters/cuda/ur_interface_loader.cpp index cfff918f90302..1f6434b2c140f 100644 --- a/unified-runtime/source/adapters/cuda/ur_interface_loader.cpp +++ b/unified-runtime/source/adapters/cuda/ur_interface_loader.cpp @@ -89,6 +89,7 @@ UR_APIEXPORT ur_result_t UR_APICALL urGetGraphExpProcAddrTable( pDdiTable->pfnDestroyExp = urGraphDestroyExp; pDdiTable->pfnExecutableGraphDestroyExp = urGraphExecutableGraphDestroyExp; pDdiTable->pfnIsEmptyExp = urGraphIsEmptyExp; + pDdiTable->pfnSetDestructionCallbackExp = urGraphSetDestructionCallbackExp; pDdiTable->pfnDumpContentsExp = urGraphDumpContentsExp; return UR_RESULT_SUCCESS; diff --git a/unified-runtime/source/adapters/hip/graph.cpp b/unified-runtime/source/adapters/hip/graph.cpp index 48f9271ce8726..fb4a0ab458439 100644 --- a/unified-runtime/source/adapters/hip/graph.cpp +++ b/unified-runtime/source/adapters/hip/graph.cpp @@ -29,6 +29,13 @@ urGraphIsEmptyExp(ur_exp_graph_handle_t /* hGraph */, bool * /* pIsEmpty */) { return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; } +UR_APIEXPORT ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + ur_exp_graph_handle_t /* hGraph */, + ur_exp_graph_destruction_callback_t /* pfnCallback */, + void * /* pUserData */) { + return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; +} + UR_APIEXPORT ur_result_t UR_APICALL urGraphDumpContentsExp( ur_exp_graph_handle_t /* hGraph */, const char * /* pDotFilePath */) { return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; diff --git a/unified-runtime/source/adapters/hip/ur_interface_loader.cpp b/unified-runtime/source/adapters/hip/ur_interface_loader.cpp index 8152546927bf5..1d6fbdf7a9c0c 100644 --- a/unified-runtime/source/adapters/hip/ur_interface_loader.cpp +++ b/unified-runtime/source/adapters/hip/ur_interface_loader.cpp @@ -89,6 +89,7 @@ UR_APIEXPORT ur_result_t UR_APICALL urGetGraphExpProcAddrTable( pDdiTable->pfnDestroyExp = urGraphDestroyExp; pDdiTable->pfnExecutableGraphDestroyExp = urGraphExecutableGraphDestroyExp; pDdiTable->pfnIsEmptyExp = urGraphIsEmptyExp; + pDdiTable->pfnSetDestructionCallbackExp = urGraphSetDestructionCallbackExp; pDdiTable->pfnDumpContentsExp = urGraphDumpContentsExp; return UR_RESULT_SUCCESS; diff --git a/unified-runtime/source/adapters/level_zero/graph.cpp b/unified-runtime/source/adapters/level_zero/graph.cpp index 58b010549b26e..f379765ca9180 100644 --- a/unified-runtime/source/adapters/level_zero/graph.cpp +++ b/unified-runtime/source/adapters/level_zero/graph.cpp @@ -44,6 +44,16 @@ ur_result_t urGraphIsEmptyExp(ur_exp_graph_handle_t /* hGraph */, return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; } +ur_result_t urGraphSetDestructionCallbackExp( + ur_exp_graph_handle_t /* hGraph */, + ur_exp_graph_destruction_callback_t /* pfnCallback */, + void * /* pUserData */) { + UR_LOG_LEGACY(ERR, + logger::LegacyMessage("[UR][L0] {} function not implemented!"), + "{} function not implemented!", __FUNCTION__); + return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; +} + ur_result_t urGraphDumpContentsExp(ur_exp_graph_handle_t /* hGraph */, const char * /* pDotFilePath */) { UR_LOG_LEGACY(ERR, diff --git a/unified-runtime/source/adapters/level_zero/platform.cpp b/unified-runtime/source/adapters/level_zero/platform.cpp index ac4c46d37f7c9..574365c92df97 100644 --- a/unified-runtime/source/adapters/level_zero/platform.cpp +++ b/unified-runtime/source/adapters/level_zero/platform.cpp @@ -613,6 +613,9 @@ ur_result_t ur_platform_handle_t_::initialize() { { {"zeCommandListGetGraphExp", reinterpret_cast(&ZeGraphExt.zeCommandListGetGraphExp)}, + {"zeGraphSetDestructionCallbackExp", + reinterpret_cast( + &ZeGraphExt.zeGraphSetDestructionCallbackExp)}, }; for (auto &[funcName, funcAddr] : ZeGraphOptionalFuncNameToAddrMap) { diff --git a/unified-runtime/source/adapters/level_zero/platform.hpp b/unified-runtime/source/adapters/level_zero/platform.hpp index 766160807a198..e6f582c78eee0 100644 --- a/unified-runtime/source/adapters/level_zero/platform.hpp +++ b/unified-runtime/source/adapters/level_zero/platform.hpp @@ -203,6 +203,9 @@ struct ur_platform_handle_t_ : ur::handle_base, ze_result_t (*zeGraphIsEmptyExp)(ze_graph_handle_t hGraph); ze_result_t (*zeGraphDumpContentsExp)(ze_graph_handle_t hGraph, const char *filePath, void *pNext); + ze_result_t (*zeGraphSetDestructionCallbackExp)( + ze_graph_handle_t hGraph, zex_mem_graph_free_callback_fn_t pfnCallback, + void *pUserData, void *pNext); } ZeGraphExt; struct ZeHostTaskExtension { diff --git a/unified-runtime/source/adapters/level_zero/ur_interface_loader.cpp b/unified-runtime/source/adapters/level_zero/ur_interface_loader.cpp index 877a4d199c44a..dc76f7729f937 100644 --- a/unified-runtime/source/adapters/level_zero/ur_interface_loader.cpp +++ b/unified-runtime/source/adapters/level_zero/ur_interface_loader.cpp @@ -275,6 +275,8 @@ UR_APIEXPORT ur_result_t UR_APICALL urGetGraphExpProcAddrTable( pDdiTable->pfnExecutableGraphDestroyExp = ur::level_zero::urGraphExecutableGraphDestroyExp; pDdiTable->pfnIsEmptyExp = ur::level_zero::urGraphIsEmptyExp; + pDdiTable->pfnSetDestructionCallbackExp = + ur::level_zero::urGraphSetDestructionCallbackExp; pDdiTable->pfnDumpContentsExp = ur::level_zero::urGraphDumpContentsExp; return result; diff --git a/unified-runtime/source/adapters/level_zero/ur_interface_loader.hpp b/unified-runtime/source/adapters/level_zero/ur_interface_loader.hpp index a4a9e9819a95e..b56cc5e803c5d 100644 --- a/unified-runtime/source/adapters/level_zero/ur_interface_loader.hpp +++ b/unified-runtime/source/adapters/level_zero/ur_interface_loader.hpp @@ -865,6 +865,9 @@ ur_result_t urQueueIsGraphCaptureEnabledExp(ur_queue_handle_t hQueue, ur_result_t urQueueGetGraphExp(ur_queue_handle_t hQueue, ur_exp_graph_handle_t *phGraph); ur_result_t urGraphIsEmptyExp(ur_exp_graph_handle_t hGraph, bool *pResult); +ur_result_t urGraphSetDestructionCallbackExp( + ur_exp_graph_handle_t hGraph, + ur_exp_graph_destruction_callback_t pfnCallback, void *pUserData); ur_result_t urGraphDumpContentsExp(ur_exp_graph_handle_t hGraph, const char *filePath); #ifdef UR_STATIC_ADAPTER_LEVEL_ZERO diff --git a/unified-runtime/source/adapters/level_zero/v2/graph.cpp b/unified-runtime/source/adapters/level_zero/v2/graph.cpp index f05da04ccd3b0..21f6960e2eebe 100644 --- a/unified-runtime/source/adapters/level_zero/v2/graph.cpp +++ b/unified-runtime/source/adapters/level_zero/v2/graph.cpp @@ -13,6 +13,8 @@ #include "common.hpp" #include "context.hpp" +#include + ur_exp_graph_handle_t_::ur_exp_graph_handle_t_(ur_context_handle_t hContext) : hContext(hContext) { ZE2UR_CALL_THROWS(hContext->getPlatform()->ZeGraphExt.zeGraphCreateExp, @@ -53,6 +55,23 @@ ur_exp_executable_graph_handle_t_::~ur_exp_executable_graph_handle_t_() { } } +namespace { + +// L0 imposes specific callback conventions. We must wrap the UR callback in +// order to not violate this requirement. +struct DestructionCallbackContext { + ur_exp_graph_destruction_callback_t callback; + void *userData; +}; + +void ZE_CALLBACK destructionCallbackWrapper(void *pUserData) { + auto *CbData = static_cast(pUserData); + CbData->callback(CbData->userData); + delete CbData; +} + +} // namespace + namespace ur::level_zero { ur_result_t urGraphCreateExp(ur_context_handle_t hContext, @@ -129,6 +148,32 @@ ur_result_t urGraphIsEmptyExp(ur_exp_graph_handle_t hGraph, bool *pIsEmpty) { return UR_RESULT_SUCCESS; } +ur_result_t urGraphSetDestructionCallbackExp( + ur_exp_graph_handle_t hGraph, + ur_exp_graph_destruction_callback_t pfnCallback, void *pUserData) { + ur_context_handle_t hContext = hGraph->getContext(); + auto ZeSetCallback = + hContext->getPlatform()->ZeGraphExt.zeGraphSetDestructionCallbackExp; + if (!checkGraphExtensionSupport(hContext) || !ZeSetCallback) { + return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; + } + + auto CbData = std::make_unique( + DestructionCallbackContext{pfnCallback, pUserData}); + + ze_result_t ZeResult = ZE_CALL_NOCHECK( + ZeSetCallback, (hGraph->getZeHandle(), destructionCallbackWrapper, + static_cast(CbData.get()), nullptr)); + + if (ZeResult != ZE_RESULT_SUCCESS) { + return ze2urResult(ZeResult); + } + + // Ownership is transfered to L0 for destruction + CbData.release(); + return UR_RESULT_SUCCESS; +} + ur_result_t urGraphDumpContentsExp(ur_exp_graph_handle_t hGraph, const char *filePath) { ur_context_handle_t hContext = hGraph->getContext(); diff --git a/unified-runtime/source/adapters/mock/ur_mockddi.cpp b/unified-runtime/source/adapters/mock/ur_mockddi.cpp index 00145e81d7afc..195702990d65c 100644 --- a/unified-runtime/source/adapters/mock/ur_mockddi.cpp +++ b/unified-runtime/source/adapters/mock/ur_mockddi.cpp @@ -12951,6 +12951,58 @@ __urdlllocal ur_result_t UR_APICALL urGraphIsEmptyExp( return exceptionToResult(std::current_exception()); } +/////////////////////////////////////////////////////////////////////////////// +/// @brief Intercept function for urGraphSetDestructionCallbackExp +__urdlllocal ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + /// [in] Handle of the graph to register the callback for. + ur_exp_graph_handle_t hGraph, + /// [in] Function pointer to the callback. The callback must not access + /// hGraph. + ur_exp_graph_destruction_callback_t pfnCallback, + /// [in][optional] Pointer to user data to be passed to the callback. The + /// user data must not reference hGraph. + void *pUserData) try { + ur_result_t result = UR_RESULT_SUCCESS; + + ur_graph_set_destruction_callback_exp_params_t params = { + &hGraph, &pfnCallback, &pUserData}; + + auto beforeCallback = reinterpret_cast( + mock::getCallbacks().get_before_callback( + "urGraphSetDestructionCallbackExp")); + if (beforeCallback) { + result = beforeCallback(¶ms); + if (result != UR_RESULT_SUCCESS) { + return result; + } + } + + auto replaceCallback = reinterpret_cast( + mock::getCallbacks().get_replace_callback( + "urGraphSetDestructionCallbackExp")); + if (replaceCallback) { + result = replaceCallback(¶ms); + } else { + + result = UR_RESULT_SUCCESS; + } + + if (result != UR_RESULT_SUCCESS) { + return result; + } + + auto afterCallback = reinterpret_cast( + mock::getCallbacks().get_after_callback( + "urGraphSetDestructionCallbackExp")); + if (afterCallback) { + return afterCallback(¶ms); + } + + return result; +} catch (...) { + return exceptionToResult(std::current_exception()); +} + /////////////////////////////////////////////////////////////////////////////// /// @brief Intercept function for urGraphDumpContentsExp __urdlllocal ur_result_t UR_APICALL urGraphDumpContentsExp( @@ -13456,6 +13508,9 @@ UR_DLLEXPORT ur_result_t UR_APICALL urGetGraphExpProcAddrTable( pDdiTable->pfnIsEmptyExp = driver::urGraphIsEmptyExp; + pDdiTable->pfnSetDestructionCallbackExp = + driver::urGraphSetDestructionCallbackExp; + pDdiTable->pfnDumpContentsExp = driver::urGraphDumpContentsExp; return result; diff --git a/unified-runtime/source/adapters/native_cpu/graph.cpp b/unified-runtime/source/adapters/native_cpu/graph.cpp index 9135921bb567c..1cbd517f0ec0f 100644 --- a/unified-runtime/source/adapters/native_cpu/graph.cpp +++ b/unified-runtime/source/adapters/native_cpu/graph.cpp @@ -33,6 +33,14 @@ urGraphIsEmptyExp(ur_exp_graph_handle_t /* hGraph */, bool * /* pIsEmpty */) { DIE_NO_IMPLEMENTATION; } +UR_APIEXPORT ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + ur_exp_graph_handle_t /* hGraph */, + ur_exp_graph_destruction_callback_t /* pfnCallback */, + void * /* pUserData */) { + + DIE_NO_IMPLEMENTATION; +} + UR_APIEXPORT ur_result_t UR_APICALL urGraphDumpContentsExp( ur_exp_graph_handle_t /* hGraph */, const char * /* pDotFilePath */) { diff --git a/unified-runtime/source/adapters/native_cpu/ur_interface_loader.cpp b/unified-runtime/source/adapters/native_cpu/ur_interface_loader.cpp index bc1183e1c9ce4..c3b121bfb0cdb 100644 --- a/unified-runtime/source/adapters/native_cpu/ur_interface_loader.cpp +++ b/unified-runtime/source/adapters/native_cpu/ur_interface_loader.cpp @@ -89,6 +89,7 @@ UR_APIEXPORT ur_result_t UR_APICALL urGetGraphExpProcAddrTable( pDdiTable->pfnDestroyExp = urGraphDestroyExp; pDdiTable->pfnExecutableGraphDestroyExp = urGraphExecutableGraphDestroyExp; pDdiTable->pfnIsEmptyExp = urGraphIsEmptyExp; + pDdiTable->pfnSetDestructionCallbackExp = urGraphSetDestructionCallbackExp; pDdiTable->pfnDumpContentsExp = urGraphDumpContentsExp; return UR_RESULT_SUCCESS; diff --git a/unified-runtime/source/adapters/offload/graph.cpp b/unified-runtime/source/adapters/offload/graph.cpp index a1346eed7cba2..d17248b4fb45b 100644 --- a/unified-runtime/source/adapters/offload/graph.cpp +++ b/unified-runtime/source/adapters/offload/graph.cpp @@ -29,6 +29,13 @@ urGraphIsEmptyExp(ur_exp_graph_handle_t /* hGraph */, bool * /* pIsEmpty */) { return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; } +UR_APIEXPORT ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + ur_exp_graph_handle_t /* hGraph */, + ur_exp_graph_destruction_callback_t /* pfnCallback */, + void * /* pUserData */) { + return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; +} + UR_APIEXPORT ur_result_t UR_APICALL urGraphDumpContentsExp( ur_exp_graph_handle_t /* hGraph */, const char * /* pDotFilePath */) { return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; diff --git a/unified-runtime/source/adapters/offload/ur_interface_loader.cpp b/unified-runtime/source/adapters/offload/ur_interface_loader.cpp index 69d9aafaf4a69..ce4ab5351a527 100644 --- a/unified-runtime/source/adapters/offload/ur_interface_loader.cpp +++ b/unified-runtime/source/adapters/offload/ur_interface_loader.cpp @@ -89,6 +89,7 @@ UR_APIEXPORT ur_result_t UR_APICALL urGetGraphExpProcAddrTable( pDdiTable->pfnDestroyExp = urGraphDestroyExp; pDdiTable->pfnExecutableGraphDestroyExp = urGraphExecutableGraphDestroyExp; pDdiTable->pfnIsEmptyExp = urGraphIsEmptyExp; + pDdiTable->pfnSetDestructionCallbackExp = urGraphSetDestructionCallbackExp; pDdiTable->pfnDumpContentsExp = urGraphDumpContentsExp; return UR_RESULT_SUCCESS; } diff --git a/unified-runtime/source/adapters/opencl/graph.cpp b/unified-runtime/source/adapters/opencl/graph.cpp index 158dbb9617c47..0db5018b96b8c 100644 --- a/unified-runtime/source/adapters/opencl/graph.cpp +++ b/unified-runtime/source/adapters/opencl/graph.cpp @@ -29,6 +29,13 @@ urGraphIsEmptyExp(ur_exp_graph_handle_t /* hGraph */, bool * /* pIsEmpty */) { return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; } +UR_APIEXPORT ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + ur_exp_graph_handle_t /* hGraph */, + ur_exp_graph_destruction_callback_t /* pfnCallback */, + void * /* pUserData */) { + return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; +} + UR_APIEXPORT ur_result_t UR_APICALL urGraphDumpContentsExp( ur_exp_graph_handle_t /* hGraph */, const char * /* pDotFilePath */) { return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; diff --git a/unified-runtime/source/adapters/opencl/ur_interface_loader.cpp b/unified-runtime/source/adapters/opencl/ur_interface_loader.cpp index 8e1c9ee94b8c3..3205af2df209e 100644 --- a/unified-runtime/source/adapters/opencl/ur_interface_loader.cpp +++ b/unified-runtime/source/adapters/opencl/ur_interface_loader.cpp @@ -504,6 +504,7 @@ UR_APIEXPORT ur_result_t UR_APICALL urGetGraphExpProcAddrTable( pDdiTable->pfnDestroyExp = urGraphDestroyExp; pDdiTable->pfnExecutableGraphDestroyExp = urGraphExecutableGraphDestroyExp; pDdiTable->pfnIsEmptyExp = urGraphIsEmptyExp; + pDdiTable->pfnSetDestructionCallbackExp = urGraphSetDestructionCallbackExp; pDdiTable->pfnDumpContentsExp = urGraphDumpContentsExp; return UR_RESULT_SUCCESS; diff --git a/unified-runtime/source/loader/layers/tracing/ur_trcddi.cpp b/unified-runtime/source/loader/layers/tracing/ur_trcddi.cpp index 6d6ac7919f4d8..5f8bfca375b61 100644 --- a/unified-runtime/source/loader/layers/tracing/ur_trcddi.cpp +++ b/unified-runtime/source/loader/layers/tracing/ur_trcddi.cpp @@ -10982,6 +10982,51 @@ __urdlllocal ur_result_t UR_APICALL urGraphIsEmptyExp( return result; } +/////////////////////////////////////////////////////////////////////////////// +/// @brief Intercept function for urGraphSetDestructionCallbackExp +__urdlllocal ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + /// [in] Handle of the graph to register the callback for. + ur_exp_graph_handle_t hGraph, + /// [in] Function pointer to the callback. The callback must not access + /// hGraph. + ur_exp_graph_destruction_callback_t pfnCallback, + /// [in][optional] Pointer to user data to be passed to the callback. The + /// user data must not reference hGraph. + void *pUserData) { + auto pfnSetDestructionCallbackExp = + getContext()->urDdiTable.GraphExp.pfnSetDestructionCallbackExp; + + if (nullptr == pfnSetDestructionCallbackExp) + return UR_RESULT_ERROR_UNSUPPORTED_FEATURE; + + ur_graph_set_destruction_callback_exp_params_t params = { + &hGraph, &pfnCallback, &pUserData}; + uint64_t instance = + getContext()->notify_begin(UR_FUNCTION_GRAPH_SET_DESTRUCTION_CALLBACK_EXP, + "urGraphSetDestructionCallbackExp", ¶ms); + + auto &logger = getContext()->logger; + UR_LOG_L(logger, INFO, " ---> urGraphSetDestructionCallbackExp\n"); + + ur_result_t result = + pfnSetDestructionCallbackExp(hGraph, pfnCallback, pUserData); + + getContext()->notify_end(UR_FUNCTION_GRAPH_SET_DESTRUCTION_CALLBACK_EXP, + "urGraphSetDestructionCallbackExp", ¶ms, &result, + instance); + + if (logger.getLevel() <= UR_LOGGER_LEVEL_INFO) { + std::ostringstream args_str; + ur::extras::printFunctionParams( + args_str, UR_FUNCTION_GRAPH_SET_DESTRUCTION_CALLBACK_EXP, ¶ms); + UR_LOG_L(logger, INFO, + " <--- urGraphSetDestructionCallbackExp({}) -> {};\n", + args_str.str(), result); + } + + return result; +} + /////////////////////////////////////////////////////////////////////////////// /// @brief Intercept function for urGraphDumpContentsExp __urdlllocal ur_result_t UR_APICALL urGraphDumpContentsExp( @@ -11638,6 +11683,11 @@ __urdlllocal ur_result_t UR_APICALL urGetGraphExpProcAddrTable( dditable.pfnIsEmptyExp = pDdiTable->pfnIsEmptyExp; pDdiTable->pfnIsEmptyExp = ur_tracing_layer::urGraphIsEmptyExp; + dditable.pfnSetDestructionCallbackExp = + pDdiTable->pfnSetDestructionCallbackExp; + pDdiTable->pfnSetDestructionCallbackExp = + ur_tracing_layer::urGraphSetDestructionCallbackExp; + dditable.pfnDumpContentsExp = pDdiTable->pfnDumpContentsExp; pDdiTable->pfnDumpContentsExp = ur_tracing_layer::urGraphDumpContentsExp; diff --git a/unified-runtime/source/loader/layers/validation/ur_valddi.cpp b/unified-runtime/source/loader/layers/validation/ur_valddi.cpp index 7b9a2f5b0982d..077e6b736846a 100644 --- a/unified-runtime/source/loader/layers/validation/ur_valddi.cpp +++ b/unified-runtime/source/loader/layers/validation/ur_valddi.cpp @@ -11761,6 +11761,38 @@ __urdlllocal ur_result_t UR_APICALL urGraphIsEmptyExp( return result; } +/////////////////////////////////////////////////////////////////////////////// +/// @brief Intercept function for urGraphSetDestructionCallbackExp +__urdlllocal ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + /// [in] Handle of the graph to register the callback for. + ur_exp_graph_handle_t hGraph, + /// [in] Function pointer to the callback. The callback must not access + /// hGraph. + ur_exp_graph_destruction_callback_t pfnCallback, + /// [in][optional] Pointer to user data to be passed to the callback. The + /// user data must not reference hGraph. + void *pUserData) { + auto pfnSetDestructionCallbackExp = + getContext()->urDdiTable.GraphExp.pfnSetDestructionCallbackExp; + + if (nullptr == pfnSetDestructionCallbackExp) { + return UR_RESULT_ERROR_UNINITIALIZED; + } + + if (getContext()->enableParameterValidation) { + if (NULL == pfnCallback) + return UR_RESULT_ERROR_INVALID_NULL_POINTER; + + if (NULL == hGraph) + return UR_RESULT_ERROR_INVALID_NULL_HANDLE; + } + + ur_result_t result = + pfnSetDestructionCallbackExp(hGraph, pfnCallback, pUserData); + + return result; +} + /////////////////////////////////////////////////////////////////////////////// /// @brief Intercept function for urGraphDumpContentsExp __urdlllocal ur_result_t UR_APICALL urGraphDumpContentsExp( @@ -12421,6 +12453,11 @@ UR_DLLEXPORT ur_result_t UR_APICALL urGetGraphExpProcAddrTable( dditable.pfnIsEmptyExp = pDdiTable->pfnIsEmptyExp; pDdiTable->pfnIsEmptyExp = ur_validation_layer::urGraphIsEmptyExp; + dditable.pfnSetDestructionCallbackExp = + pDdiTable->pfnSetDestructionCallbackExp; + pDdiTable->pfnSetDestructionCallbackExp = + ur_validation_layer::urGraphSetDestructionCallbackExp; + dditable.pfnDumpContentsExp = pDdiTable->pfnDumpContentsExp; pDdiTable->pfnDumpContentsExp = ur_validation_layer::urGraphDumpContentsExp; diff --git a/unified-runtime/source/loader/loader.def.in b/unified-runtime/source/loader/loader.def.in index 8faa938b745f0..cc6ec03371edb 100644 --- a/unified-runtime/source/loader/loader.def.in +++ b/unified-runtime/source/loader/loader.def.in @@ -145,6 +145,7 @@ EXPORTS urGraphExecutableGraphDestroyExp urGraphInstantiateGraphExp urGraphIsEmptyExp + urGraphSetDestructionCallbackExp urIPCCloseMemHandleExp urIPCGetMemHandleExp urIPCOpenMemHandleExp @@ -389,6 +390,7 @@ EXPORTS urPrintGraphExecutableGraphDestroyExpParams urPrintGraphInstantiateGraphExpParams urPrintGraphIsEmptyExpParams + urPrintGraphSetDestructionCallbackExpParams urPrintImageChannelOrder urPrintImageChannelType urPrintImageDesc diff --git a/unified-runtime/source/loader/loader.map.in b/unified-runtime/source/loader/loader.map.in index b7f9f3428a5f5..9ffb00211b6dc 100644 --- a/unified-runtime/source/loader/loader.map.in +++ b/unified-runtime/source/loader/loader.map.in @@ -145,6 +145,7 @@ urGraphExecutableGraphDestroyExp; urGraphInstantiateGraphExp; urGraphIsEmptyExp; + urGraphSetDestructionCallbackExp; urIPCCloseMemHandleExp; urIPCGetMemHandleExp; urIPCOpenMemHandleExp; @@ -389,6 +390,7 @@ urPrintGraphExecutableGraphDestroyExpParams; urPrintGraphInstantiateGraphExpParams; urPrintGraphIsEmptyExpParams; + urPrintGraphSetDestructionCallbackExpParams; urPrintImageChannelOrder; urPrintImageChannelType; urPrintImageDesc; diff --git a/unified-runtime/source/loader/ur_ldrddi.cpp b/unified-runtime/source/loader/ur_ldrddi.cpp index 7ac726f9c9cb1..a68a92742892e 100644 --- a/unified-runtime/source/loader/ur_ldrddi.cpp +++ b/unified-runtime/source/loader/ur_ldrddi.cpp @@ -6227,6 +6227,29 @@ __urdlllocal ur_result_t UR_APICALL urGraphIsEmptyExp( return pfnIsEmptyExp(hGraph, pResult); } +/////////////////////////////////////////////////////////////////////////////// +/// @brief Intercept function for urGraphSetDestructionCallbackExp +__urdlllocal ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + /// [in] Handle of the graph to register the callback for. + ur_exp_graph_handle_t hGraph, + /// [in] Function pointer to the callback. The callback must not access + /// hGraph. + ur_exp_graph_destruction_callback_t pfnCallback, + /// [in][optional] Pointer to user data to be passed to the callback. The + /// user data must not reference hGraph. + void *pUserData) { + + auto *dditable = *reinterpret_cast(hGraph); + + auto *pfnSetDestructionCallbackExp = + dditable->GraphExp.pfnSetDestructionCallbackExp; + if (nullptr == pfnSetDestructionCallbackExp) + return UR_RESULT_ERROR_UNINITIALIZED; + + // forward to device-platform + return pfnSetDestructionCallbackExp(hGraph, pfnCallback, pUserData); +} + /////////////////////////////////////////////////////////////////////////////// /// @brief Intercept function for urGraphDumpContentsExp __urdlllocal ur_result_t UR_APICALL urGraphDumpContentsExp( @@ -6817,6 +6840,8 @@ UR_DLLEXPORT ur_result_t UR_APICALL urGetGraphExpProcAddrTable( pDdiTable->pfnExecutableGraphDestroyExp = ur_loader::urGraphExecutableGraphDestroyExp; pDdiTable->pfnIsEmptyExp = ur_loader::urGraphIsEmptyExp; + pDdiTable->pfnSetDestructionCallbackExp = + ur_loader::urGraphSetDestructionCallbackExp; pDdiTable->pfnDumpContentsExp = ur_loader::urGraphDumpContentsExp; } else { // return pointers directly to platform's DDIs diff --git a/unified-runtime/source/loader/ur_libapi.cpp b/unified-runtime/source/loader/ur_libapi.cpp index 51f6ca8ee556f..1188978674036 100644 --- a/unified-runtime/source/loader/ur_libapi.cpp +++ b/unified-runtime/source/loader/ur_libapi.cpp @@ -11346,6 +11346,37 @@ ur_result_t UR_APICALL urGraphIsEmptyExp( return exceptionToResult(std::current_exception()); } +/////////////////////////////////////////////////////////////////////////////// +/// @brief Register a callback to be invoked when the graph is destroyed. +/// +/// @returns +/// - ::UR_RESULT_SUCCESS +/// - ::UR_RESULT_ERROR_UNINITIALIZED +/// - ::UR_RESULT_ERROR_DEVICE_LOST +/// - ::UR_RESULT_ERROR_ADAPTER_SPECIFIC +/// - ::UR_RESULT_ERROR_INVALID_NULL_HANDLE +/// + `NULL == hGraph` +/// - ::UR_RESULT_ERROR_INVALID_NULL_POINTER +/// + `NULL == pfnCallback` +ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + /// [in] Handle of the graph to register the callback for. + ur_exp_graph_handle_t hGraph, + /// [in] Function pointer to the callback. The callback must not access + /// hGraph. + ur_exp_graph_destruction_callback_t pfnCallback, + /// [in][optional] Pointer to user data to be passed to the callback. The + /// user data must not reference hGraph. + void *pUserData) try { + auto pfnSetDestructionCallbackExp = + ur_lib::getContext()->urDdiTable.GraphExp.pfnSetDestructionCallbackExp; + if (nullptr == pfnSetDestructionCallbackExp) + return UR_RESULT_ERROR_UNINITIALIZED; + + return pfnSetDestructionCallbackExp(hGraph, pfnCallback, pUserData); +} catch (...) { + return exceptionToResult(std::current_exception()); +} + /////////////////////////////////////////////////////////////////////////////// /// @brief Dump the contents of the recorded graph to the provided file path. /// diff --git a/unified-runtime/source/loader/ur_print.cpp b/unified-runtime/source/loader/ur_print.cpp index f500efa52e864..8c56e882cde85 100644 --- a/unified-runtime/source/loader/ur_print.cpp +++ b/unified-runtime/source/loader/ur_print.cpp @@ -2155,6 +2155,14 @@ ur_result_t urPrintGraphIsEmptyExpParams( return str_copy(&ss, buffer, buff_size, out_size); } +ur_result_t urPrintGraphSetDestructionCallbackExpParams( + const struct ur_graph_set_destruction_callback_exp_params_t *params, + char *buffer, const size_t buff_size, size_t *out_size) { + std::stringstream ss; + ss << params; + return str_copy(&ss, buffer, buff_size, out_size); +} + ur_result_t urPrintGraphDumpContentsExpParams( const struct ur_graph_dump_contents_exp_params_t *params, char *buffer, const size_t buff_size, size_t *out_size) { diff --git a/unified-runtime/source/ur_api.cpp b/unified-runtime/source/ur_api.cpp index 81dcda1a4fcb6..d6b7be91aa80d 100644 --- a/unified-runtime/source/ur_api.cpp +++ b/unified-runtime/source/ur_api.cpp @@ -9850,6 +9850,31 @@ ur_result_t UR_APICALL urGraphIsEmptyExp( return result; } +/////////////////////////////////////////////////////////////////////////////// +/// @brief Register a callback to be invoked when the graph is destroyed. +/// +/// @returns +/// - ::UR_RESULT_SUCCESS +/// - ::UR_RESULT_ERROR_UNINITIALIZED +/// - ::UR_RESULT_ERROR_DEVICE_LOST +/// - ::UR_RESULT_ERROR_ADAPTER_SPECIFIC +/// - ::UR_RESULT_ERROR_INVALID_NULL_HANDLE +/// + `NULL == hGraph` +/// - ::UR_RESULT_ERROR_INVALID_NULL_POINTER +/// + `NULL == pfnCallback` +ur_result_t UR_APICALL urGraphSetDestructionCallbackExp( + /// [in] Handle of the graph to register the callback for. + ur_exp_graph_handle_t hGraph, + /// [in] Function pointer to the callback. The callback must not access + /// hGraph. + ur_exp_graph_destruction_callback_t pfnCallback, + /// [in][optional] Pointer to user data to be passed to the callback. The + /// user data must not reference hGraph. + void *pUserData) { + ur_result_t result = UR_RESULT_SUCCESS; + return result; +} + /////////////////////////////////////////////////////////////////////////////// /// @brief Dump the contents of the recorded graph to the provided file path. /// diff --git a/unified-runtime/test/conformance/exp_graph/CMakeLists.txt b/unified-runtime/test/conformance/exp_graph/CMakeLists.txt index a59b9d082d5e4..a51c14b3d8ee8 100644 --- a/unified-runtime/test/conformance/exp_graph/CMakeLists.txt +++ b/unified-runtime/test/conformance/exp_graph/CMakeLists.txt @@ -9,6 +9,7 @@ add_conformance_devices_test(exp_graph urGraphDumpContentsExp.cpp urGraphInstantiateGraphExp.cpp urGraphIsEmptyExp.cpp + urGraphSetDestructionCallbackExp.cpp urQueueBeginCaptureIntoGraphExp.cpp urQueueBeginGraphCaptureExp.cpp urQueueEndGraphCaptureExp.cpp diff --git a/unified-runtime/test/conformance/exp_graph/urGraphSetDestructionCallbackExp.cpp b/unified-runtime/test/conformance/exp_graph/urGraphSetDestructionCallbackExp.cpp new file mode 100644 index 0000000000000..e766ccb494018 --- /dev/null +++ b/unified-runtime/test/conformance/exp_graph/urGraphSetDestructionCallbackExp.cpp @@ -0,0 +1,79 @@ +// Part of the LLVM Project, under the Apache License v2.0 with LLVM +// Exceptions. See https://llvm.org/LICENSE.txt for license information. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "fixtures.h" + +struct urGraphSetDestructionCallbackExpTest : uur::urGraphSupportedExpTest { + void SetUp() override { + UUR_RETURN_ON_FATAL_FAILURE(urGraphSupportedExpTest::SetUp()); + std::tuple minL0DriverVersion = {1, 15, 37561}; + SKIP_IF_DRIVER_TOO_OLD("Level-Zero", minL0DriverVersion, platform, device); + ASSERT_SUCCESS(urGraphCreateExp(context, &graph)); + } + + void TearDown() override { + if (graph) { + urGraphDestroyExp(graph); + } + UUR_RETURN_ON_FATAL_FAILURE(urGraphSupportedExpTest::TearDown()); + } + + ur_exp_graph_handle_t graph = nullptr; +}; + +UUR_DEVICE_TEST_SUITE_WITH_QUEUE_TYPES( + urGraphSetDestructionCallbackExpTest, + ::testing::Values(0 /* In-Order */, + UR_QUEUE_FLAG_OUT_OF_ORDER_EXEC_MODE_ENABLE)); + +TEST_P(urGraphSetDestructionCallbackExpTest, SuccessFreeMemory) { + int *data = static_cast(malloc(sizeof(int))); + ASSERT_NE(data, nullptr); + *data = 42; + + ur_exp_graph_destruction_callback_t callback = +[](void *pUserData) { + void **pCastedData = static_cast(pUserData); + free(*pCastedData); + *pCastedData = nullptr; + }; + ASSERT_SUCCESS(urGraphSetDestructionCallbackExp(graph, callback, + static_cast(&data))); + + ASSERT_NE(data, nullptr); + ASSERT_EQ(*data, 42); + ASSERT_SUCCESS(urGraphDestroyExp(graph)); + graph = nullptr; + ASSERT_EQ(data, nullptr); +} + +TEST_P(urGraphSetDestructionCallbackExpTest, SuccessMultipleCallbacks) { + bool firstInvoked = false; + bool secondInvoked = false; + + ur_exp_graph_destruction_callback_t callback = + +[](void *pUserData) { *static_cast(pUserData) = true; }; + ASSERT_SUCCESS( + urGraphSetDestructionCallbackExp(graph, callback, &firstInvoked)); + ASSERT_SUCCESS( + urGraphSetDestructionCallbackExp(graph, callback, &secondInvoked)); + + ASSERT_FALSE(firstInvoked); + ASSERT_FALSE(secondInvoked); + ASSERT_SUCCESS(urGraphDestroyExp(graph)); + graph = nullptr; + ASSERT_TRUE(firstInvoked); + ASSERT_TRUE(secondInvoked); +} + +TEST_P(urGraphSetDestructionCallbackExpTest, InvalidNullHandleGraph) { + ASSERT_EQ_RESULT( + UR_RESULT_ERROR_INVALID_NULL_HANDLE, + urGraphSetDestructionCallbackExp(nullptr, [](void *) {}, nullptr)); +} + +TEST_P(urGraphSetDestructionCallbackExpTest, InvalidNullPointerCallback) { + ASSERT_EQ_RESULT(UR_RESULT_ERROR_INVALID_NULL_POINTER, + urGraphSetDestructionCallbackExp(graph, nullptr, nullptr)); +} From a67357044db1f3a6530a96891fa95f8473749e25 Mon Sep 17 00:00:00 2001 From: Matthew Michel Date: Fri, 22 May 2026 06:38:19 -0700 Subject: [PATCH 2/2] [UR][L0v2] Add spec and implementation for urGraphSetDestructionCallbackExp --- unified-runtime/source/adapters/level_zero/v2/graph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified-runtime/source/adapters/level_zero/v2/graph.cpp b/unified-runtime/source/adapters/level_zero/v2/graph.cpp index 21f6960e2eebe..d2fdfa50ba65a 100644 --- a/unified-runtime/source/adapters/level_zero/v2/graph.cpp +++ b/unified-runtime/source/adapters/level_zero/v2/graph.cpp @@ -169,7 +169,7 @@ ur_result_t urGraphSetDestructionCallbackExp( return ze2urResult(ZeResult); } - // Ownership is transfered to L0 for destruction + // Ownership is transfered to the graph destruction callback for deletion CbData.release(); return UR_RESULT_SUCCESS; }