Repository: microsoft/onnxruntime
File: onnxruntime/core/providers/nv_tensorrt_rtx/nv_execution_provider_custom_ops.cc (C++)

Code before:
    LOGS_DEFAULT(WARNING) << "[NvTensorRTRTX EP] Failed to get TRT plugins from TRT plugin registration. Therefore, TRT EP can't create custom ops for TRT plugins";
  }

  // Register native custom ops (register these independent of TRT plugin library availability)
  if (!native_custom_ops_initialized) {
    const char* native_custom_ops_names[] = {"TRT_FP4DynamicQuantize", "TRT_FP8QuantizeLinear", "TRT_FP8DequantizeLinear"};
    size_t num_native_custom_ops = std::size(native_custom_ops_names);

    for (size_t i = 0; i < num_native_custom_ops; i++) {
      native_custom_op_list.push_back(std::make_unique<TensorRTCustomOp>(onnxruntime::kNvTensorRTRTXExecutionProvider, nullptr));
      native_custom_op_list.back()->SetName(native_custom_ops_names[i]);
      native_custom_op_domain->custom_ops_.push_back(native_custom_op_list.back().get());
    }

    native_custom_op_domain->domain_ = "trt";
    domain_list.push_back(native_custom_op_domain.get());
    native_custom_ops_initialized = true;
  }

  return Status::OK();
}

void ReleaseTensorRTCustomOpDomain(OrtCustomOpDomain* domain) {
  (void)domain;  // Suppress unused parameter warning
  // The domain and its custom ops are owned by static unique_ptrs in CreateTensorRTCustomOpDomainList().
  // Callers receive raw pointers via .get().
  //  1. Manually deleting them would cause a double-free when the static unique_ptrs are destroyed at program exit.
  //  2. Resetting the static unique_ptrs is also unsafe because other EP instances or InferenceSession objects
  //     may still hold raw pointers to these same objects (handed out via domain_list).
  // The static objects would be shared across EP instances and would persist for the program lifetime.
}

void ReleaseTensorRTCustomOpDomainList(std::vector<OrtCustomOpDomain*>& custom_op_domain_list) {
  // Only clear the reference vector, don't delete the static domain objects.
  custom_op_domain_list.clear();
}

}  // namespace onnxruntime


Review comment (bug):
Would this indicate a different problem of someone calling to destroy objects that are in-use? Should we fix that bug?

Another question, static objects would be destroyed just prior to this DLL being unloaded. We want to make sure that the entities being destroyed do not refer to another DLL that could potentially be unloaded first.

It is for the reason people usually introduce a special API to have control of the process and to destroy things at a safe time and not to delegate it to a OS dependent specifics when shared objects are unloaded and the order of static destruction.

Code after:

  // Register native custom ops (register these independent of TRT plugin library availability)
  if (native_custom_op_list.empty()) {
    const char* native_custom_ops_names[] = {"TRT_FP4DynamicQuantize", "TRT_FP8QuantizeLinear", "TRT_FP8DequantizeLinear"};
    size_t num_native_custom_ops = std::size(native_custom_ops_names);

    for (size_t i = 0; i < num_native_custom_ops; i++) {
      native_custom_op_list.push_back(std::make_unique<TensorRTCustomOp>(onnxruntime::kNvTensorRTRTXExecutionProvider, nullptr));
      native_custom_op_list.back()->SetName(native_custom_ops_names[i]);
      native_custom_op_domain->custom_ops_.push_back(native_custom_op_list.back().get());
    }

    native_custom_op_domain->domain_ = "trt";
    domain_list.push_back(native_custom_op_domain.get());
  }

  return Status::OK();
}

void ReleaseTensorRTCustomOpDomain(OrtCustomOpDomain* domain) {
  (void)domain;  // Suppress unused parameter warning
  // The domain and its custom ops are owned by static unique_ptrs in CreateTensorRTCustomOpDomainList().
  // Callers receive raw pointers via .get().
  //  1. Manually deleting them would cause a double-free when the static unique_ptrs are destroyed at program exit.
  //  2. Resetting the static unique_ptrs is also unsafe because other EP instances or InferenceSession objects
  //     may still hold raw pointers to these same objects (handed out via domain_list).
  // The static objects would be shared across EP instances and would persist for the program lifetime.
}

void ReleaseTensorRTCustomOpDomainList(std::vector<OrtCustomOpDomain*>& custom_op_domain_list) {
  // Only clear the reference vector, don't delete the static domain objects.
  custom_op_domain_list.clear();
}

}  // namespace onnxruntime