From 29a70cdbcee37133414a41ac4e4e9aacb45bb1c1 Mon Sep 17 00:00:00 2001 From: Changming Sun Date: Thu, 24 Aug 2023 23:07:02 -0700 Subject: [PATCH] nodejs: Release Ort Env before main function returns (#17288) ### Description Release OrtEnv before main function returns. Before this change, OrtEnv is deleted when C/C++ runtime destructs all global variables in ONNX Runtime's core framework. The callstack is like this: ``` * frame #0: 0x00007fffee39f5a6 libonnxruntime.so.1.16.0`onnxruntime::Environment::~Environment(this=0x00007fffee39fbf2) at environment.h:20:7 frame #1: 0x00007fffee39f614 libonnxruntime.so.1.16.0`std::default_delete::operator()(this=0x00007ffff4c30e50, __ptr=0x0000000005404b00) const at unique_ptr.h:85:2 frame #2: 0x00007fffee39edca libonnxruntime.so.1.16.0`std::unique_ptr>::~unique_ptr(this=0x5404b00) at unique_ptr.h:361:17 frame #3: 0x00007fffee39e2ab libonnxruntime.so.1.16.0`OrtEnv::~OrtEnv(this=0x00007ffff4c30e50) at ort_env.cc:43:1 frame #4: 0x00007fffee39fa96 libonnxruntime.so.1.16.0`std::default_delete::operator()(this=0x00007fffefff8f78, __ptr=0x00007ffff4c30e50) const at unique_ptr.h:85:2 frame #5: 0x00007fffee39f394 libonnxruntime.so.1.16.0`std::unique_ptr>::~unique_ptr(this=0x7ffff4c30e50) at unique_ptr.h:361:17 frame #6: 0x00007ffff78574b5 libc.so.6`__run_exit_handlers + 261 frame #7: 0x00007ffff7857630 libc.so.6`exit + 32 frame #8: 0x00007ffff783feb7 libc.so.6`__libc_start_call_main + 135 frame #9: 0x00007ffff783ff60 libc.so.6`__libc_start_main@@GLIBC_2.34 + 128 frame #10: 0x0000000000abbdee node`_start + 46 ``` After this change, OrtEnv will be deleted before the main function returns and nodejs is still alive. --- node/script/build.ts | 2 +- node/src/inference_session_wrap.cc | 17 ++++++++++------- node/src/inference_session_wrap.h | 4 ---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/node/script/build.ts b/node/script/build.ts index 3453bda398a1c..95dacd076d3a3 100644 --- a/node/script/build.ts +++ b/node/script/build.ts @@ -41,7 +41,7 @@ const args = [ 'cmake-js', (REBUILD ? 'reconfigure' : 'configure'), `--arch=${ARCH}`, - '--CDnapi_build_version=3', + '--CDnapi_build_version=6', `--CDCMAKE_BUILD_TYPE=${CONFIG}`, ]; if (ONNXRUNTIME_BUILD_DIR && typeof ONNXRUNTIME_BUILD_DIR === 'string') { diff --git a/node/src/inference_session_wrap.cc b/node/src/inference_session_wrap.cc index 78f32ec09250b..9f235d29d20b7 100644 --- a/node/src/inference_session_wrap.cc +++ b/node/src/inference_session_wrap.cc @@ -10,13 +10,16 @@ #include "tensor_helper.h" Napi::FunctionReference InferenceSessionWrap::constructor; -Ort::Env *InferenceSessionWrap::ortEnv; Napi::Object InferenceSessionWrap::Init(Napi::Env env, Napi::Object exports) { // create ONNX runtime env Ort::InitApi(); - ortEnv = new Ort::Env{ORT_LOGGING_LEVEL_WARNING, "onnxruntime-node"}; - + ORT_NAPI_THROW_ERROR_IF( + Ort::Global::api_ == nullptr, env, + "Failed to initialize ONNX Runtime API. It could happen when this nodejs binding was built with a higher version " + "ONNX Runtime but now runs with a lower version ONNX Runtime DLL(or shared library)."); + auto ortEnv = new Ort::Env{ORT_LOGGING_LEVEL_WARNING, "onnxruntime-node"}; + env.SetInstanceData(ortEnv); // initialize binding Napi::HandleScope scope(env); @@ -28,7 +31,6 @@ Napi::Object InferenceSessionWrap::Init(Napi::Env env, Napi::Object exports) { constructor = Napi::Persistent(func); constructor.SuppressDestruct(); - exports.Set("InferenceSession", func); return exports; } @@ -54,7 +56,7 @@ Napi::Value InferenceSessionWrap::LoadModel(const Napi::CallbackInfo &info) { Napi::String value = info[0].As(); ParseSessionOptions(info[1].As(), sessionOptions); - this->session_.reset(new Ort::Session(OrtEnv(), + this->session_.reset(new Ort::Session(*env.GetInstanceData(), #ifdef _WIN32 reinterpret_cast(value.Utf16Value().c_str()), #else @@ -69,8 +71,9 @@ Napi::Value InferenceSessionWrap::LoadModel(const Napi::CallbackInfo &info) { int64_t bytesLength = info[2].As().Int64Value(); ParseSessionOptions(info[1].As(), sessionOptions); - this->session_.reset( - new Ort::Session(OrtEnv(), reinterpret_cast(buffer) + bytesOffset, bytesLength, sessionOptions)); + this->session_.reset(new Ort::Session(*env.GetInstanceData(), + reinterpret_cast(buffer) + bytesOffset, bytesLength, + sessionOptions)); } else { ORT_NAPI_THROW_TYPEERROR( env, diff --git a/node/src/inference_session_wrap.h b/node/src/inference_session_wrap.h index 8298819288947..1a51a70a836cf 100644 --- a/node/src/inference_session_wrap.h +++ b/node/src/inference_session_wrap.h @@ -54,10 +54,6 @@ class InferenceSessionWrap : public Napi::ObjectWrap { // persistent constructor static Napi::FunctionReference constructor; - // global env - static Ort::Env *ortEnv; - static Ort::Env &OrtEnv() { return *ortEnv; } - // session objects bool initialized_; std::unique_ptr session_;