77#include < fstream>
88#include < string>
99#include " QnnOpDef.h"
10- #include " HTP/QnnHtpPerfInfrastructure.h"
11- #include " HTP/QnnHtpSystemContext.h"
1210#include " CPU/QnnCpuCommon.h"
1311// TODO: not exist for Windows yet
1412// #include "GPU/QnnGpuCommon.h"
1513#include " DSP/QnnDspCommon.h"
1614#include " HTP/QnnHtpCommon.h"
1715#include " HTP/QnnHtpContext.h"
16+ #include " HTP/QnnHtpPerfInfrastructure.h"
17+ #include " HTP/QnnHtpSystemContext.h"
1818#include " Saver/QnnSaver.h"
1919#include < gsl/gsl>
2020#include " core/framework/endian_utils.h"
2121#include " core/common/logging/capture.h"
22+ #include " core/providers/qnn/qnn_allocator.h"
2223#include " core/providers/qnn/builder/onnx_ctx_model_helper.h"
2324#include " core/providers/qnn/builder/qnn_configs_helper.h"
25+ #include " core/providers/qnn/builder/qnn_utils.h"
2426
2527#ifdef _WIN32
2628#include < winmeta.h>
@@ -46,6 +48,14 @@ static Qnn_Version_t GetQnnInterfaceApiVersion(const QnnSystemInterface_t* qnn_i
4648 return qnn_interface->systemApiVersion ;
4749}
4850
51+ static const char * DlError () {
52+ #ifdef _WIN32
53+ return " " ;
54+ #else
55+ return ::dlerror ();
56+ #endif
57+ }
58+
4959template <typename F, class T >
5060Status QnnBackendManager::GetQnnInterfaceProvider (const char * lib_path,
5161 const char * interface_provider_name,
@@ -545,10 +555,11 @@ Status QnnBackendManager::CreateContext() {
545555 device_handle_,
546556 context_configs,
547557 &context);
548- contexts_.push_back (context);
549558
550559 ORT_RETURN_IF (QNN_CONTEXT_NO_ERROR != result, " Failed to create context. Error: " , QnnErrorHandleToString (result));
551560
561+ ORT_RETURN_IF_ERROR (AddQnnContextHandle (context));
562+
552563 context_created_ = true ;
553564 return Status::OK ();
554565}
@@ -558,14 +569,9 @@ Status QnnBackendManager::ReleaseContext() {
558569 return Status::OK ();
559570 }
560571
561- bool failed = false ;
562- for (auto context : contexts_) {
563- Qnn_ErrorHandle_t result = qnn_interface_.contextFree (context, nullptr );
564- if (QNN_CONTEXT_NO_ERROR != result) {
565- failed = true ;
566- }
567- }
568- ORT_RETURN_IF (failed, " Failed to release context." );
572+ // release QNN context handles
573+ contexts_.clear ();
574+ context_map_.clear ();
569575
570576 context_created_ = false ;
571577 return Status::OK ();
@@ -766,7 +772,7 @@ Status QnnBackendManager::LoadCachedQnnContextFromBuffer(char* buffer, uint64_t
766772 &context,
767773 profile_backend_handle_);
768774 ORT_RETURN_IF (QNN_SUCCESS != rt, " Failed to create context from binary. Error code: " , rt);
769- contexts_. push_back ( context);
775+ ORT_RETURN_IF_ERROR ( AddQnnContextHandle ( context) );
770776 if (1 == graph_count) {
771777 // in case the EPContext node is generated from script
772778 // the graph name from the context binary may not match the EPContext node name
@@ -1452,13 +1458,8 @@ const char* QnnBackendManager::QnnProfileErrorToString(QnnProfile_Error_t error)
14521458 }
14531459}
14541460
1455- const char * QnnBackendManager::QnnErrorHandleToString (Qnn_ErrorHandle_t error) {
1456- // From QNN SDK: The memory is statically owned and should not be freed by the caller.
1457- const char * error_msg = nullptr ;
1458- if (QNN_SUCCESS == qnn_interface_.errorGetMessage (error, &error_msg)) {
1459- return error_msg;
1460- }
1461- return " Unknown" ;
1461+ std::string_view QnnBackendManager::QnnErrorHandleToString (Qnn_ErrorHandle_t error) {
1462+ return utils::GetQnnErrorMessage (qnn_interface_, error);
14621463}
14631464
14641465const std::string QnnBackendManager::ExtractQnnScalarValue (const Qnn_Scalar_t& scalar) {
@@ -1691,5 +1692,90 @@ void* QnnBackendManager::LibFunction(void* handle, const char* symbol, std::stri
16911692#endif
16921693}
16931694
1695+ Status QnnBackendManager::AddQnnContextHandle (Qnn_ContextHandle_t raw_context_handle) {
1696+ ORT_RETURN_IF (logger_ == nullptr , " logger_ should be set." );
1697+
1698+ auto free_context_handle = [this , &logger = *logger_](Qnn_ContextHandle_t raw_context_handle) {
1699+ const auto free_result = qnn_interface_.contextFree (raw_context_handle, nullptr );
1700+ if (free_result != QNN_CONTEXT_NO_ERROR) {
1701+ LOGS (logger, ERROR) << " qnn_interface.contextFree() failed: "
1702+ << utils::GetVerboseQnnErrorMessage (qnn_interface_, free_result);
1703+ }
1704+ };
1705+
1706+ // take ownership of `raw_context_handle`
1707+ auto context_handle = UniqueQnnContextHandle (raw_context_handle, free_context_handle);
1708+ auto mem_handle_manager = std::make_unique<QnnContextMemHandleManager>(GetQnnInterface (), raw_context_handle,
1709+ *logger_);
1710+
1711+ auto context_handle_record = std::make_shared<QnnContextHandleRecord>();
1712+ context_handle_record->context_handle = std::move (context_handle);
1713+ context_handle_record->mem_handles = std::move (mem_handle_manager);
1714+
1715+ const bool inserted = context_map_.try_emplace (raw_context_handle, std::move (context_handle_record)).second ;
1716+ ORT_RETURN_IF_NOT (inserted, " QNN context was already added: " , raw_context_handle);
1717+
1718+ contexts_.push_back (raw_context_handle);
1719+
1720+ return Status::OK ();
1721+ }
1722+
1723+ Status QnnBackendManager::GetOrRegisterContextMemHandle (Qnn_ContextHandle_t context_handle,
1724+ void * shared_memory_address,
1725+ const Qnn_Tensor_t& qnn_tensor,
1726+ Qnn_MemHandle_t& mem_handle) {
1727+ // Multi-threading situations to consider:
1728+ // 1) Shared memory allocation is being freed in another thread while we are processing `shared_memory_address`.
1729+ // This implies incorrect usage as the memory is being freed while it is still in use. Let's assume this won't
1730+ // happen.
1731+ // 2) The shared memory allocation clean up function is being run from another thread while the
1732+ // QnnContextHandleRecord or QnnBackendManager objects are being destroyed.
1733+ // Usage of weak_ptrs from the clean up function should ensure that those objects are only accessed while they are
1734+ // in scope.
1735+
1736+ const auto context_handle_record_it = context_map_.find (context_handle);
1737+ ORT_RETURN_IF_NOT (context_handle_record_it != context_map_.end (), " QNN context not found: " , context_handle);
1738+
1739+ auto & context_handle_record = context_handle_record_it->second ;
1740+ auto & context_mem_handle_manager = context_handle_record->mem_handles ;
1741+
1742+ bool did_register{};
1743+ ORT_RETURN_IF_ERROR (context_mem_handle_manager->GetOrRegister (shared_memory_address, qnn_tensor,
1744+ mem_handle, did_register));
1745+
1746+ if (did_register) {
1747+ HtpSharedMemoryAllocator::AllocationCleanUpFn unregister_mem_handle =
1748+ [&logger = *logger_,
1749+ weak_backend_manager = weak_from_this (),
1750+ weak_context_handle_record = std::weak_ptr{context_handle_record}](
1751+ void * shared_memory_address) {
1752+ // Lock QnnBackendManager shared_ptr to ensure that QNN interface is still valid.
1753+ auto backend_manager = weak_backend_manager.lock ();
1754+ if (!backend_manager) {
1755+ return ;
1756+ }
1757+
1758+ // Lock QnnContextHandleRecord shared_ptr to ensure that QNN context handle is still valid.
1759+ auto context_handle_record = weak_context_handle_record.lock ();
1760+ if (!context_handle_record) {
1761+ return ;
1762+ }
1763+
1764+ auto & context_mem_handle_manager = context_handle_record->mem_handles ;
1765+
1766+ auto unregister_status = context_mem_handle_manager->Unregister (shared_memory_address);
1767+ if (!unregister_status.IsOK ()) {
1768+ LOGS (logger, ERROR) << " Failed to unregister shared memory mem handle for address: "
1769+ << shared_memory_address << " , error: " << unregister_status.ErrorMessage ();
1770+ }
1771+ };
1772+
1773+ ORT_RETURN_IF_ERROR (HtpSharedMemoryAllocator::AddAllocationCleanUp (shared_memory_address,
1774+ std::move (unregister_mem_handle)));
1775+ }
1776+
1777+ return Status::OK ();
1778+ }
1779+
16941780} // namespace qnn
16951781} // namespace onnxruntime
0 commit comments