Skip to content

Commit 0d3d675

Browse files
committed
Fix NPU segfault with two concurrent inference pipelines
When two gvadetect pipelines target device=NPU simultaneously the NPU VCL compiler crashes with "ConvertVPUMI37XX2ELF failed: bad optional access" followed by SIGSEGV. Root causes and fixes: 1. ov::Core::compile_model() is not re-entrant for the NPU plugin. The existing Meyers-singleton ov::Core is thread-safe to construct but concurrent compile_model calls race inside the NPU driver. Added a compile_mutex that serializes only compile_model and remote-context creation — read_model is intentionally left unserialized since it is a pure XML parse with no device contact. 2. gvafpscounter accesses its global fps_counters map without holding channels_mutex in create_average, create_writepipe, create_readpipe, new_frame and eos. Added the missing lock_guard in each. 3. NPU does not support batch-size > 1. Requesting batch-size=32 triggers a compiler crash even on a single pipeline. Added a clamp to 1 with a warning when device contains "NPU". Tested: 5/5 concurrent dual-pipeline runs pass (0/5 before fix). Single pipeline: 90 FPS, no regression. Dual pipeline: 44 FPS each.
1 parent 5b22540 commit 0d3d675

2 files changed

Lines changed: 36 additions & 19 deletions

File tree

src/gst/elements/gvafpscounter/fpscounter_c.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ void fps_counter_create_iterative(const char *intervals, bool print_std_dev, boo
4141

4242
void fps_counter_create_average(unsigned int starting_frame, unsigned int interval) {
4343
try {
44+
std::lock_guard<std::mutex> lock(channels_mutex);
4445
if (not fps_counters.count("average")) {
45-
std::shared_ptr<FpsCounter> fps_counter = nullptr;
46-
fps_counter = std::make_shared<IterativeFpsCounter>(starting_frame, interval, true, false, false);
47-
fps_counters.insert({"average", fps_counter});
46+
fps_counters.insert({"average",
47+
std::make_shared<IterativeFpsCounter>(starting_frame, interval, true, false, false)});
4848
}
4949
} catch (std::exception &e) {
5050
GVA_ERROR("Error during creation average fpscounter: %s", Utils::createNestedErrorMsg(e).c_str());
@@ -53,10 +53,9 @@ void fps_counter_create_average(unsigned int starting_frame, unsigned int interv
5353

5454
void fps_counter_create_writepipe(const char *pipe_name) {
5555
try {
56+
std::lock_guard<std::mutex> lock(channels_mutex);
5657
if (not fps_counters.count("writepipe")) {
57-
std::shared_ptr<FpsCounter> fps_counter = nullptr;
58-
fps_counter = std::make_shared<WritePipeFpsCounter>(pipe_name);
59-
fps_counters.insert({"writepipe", fps_counter});
58+
fps_counters.insert({"writepipe", std::make_shared<WritePipeFpsCounter>(pipe_name)});
6059
}
6160
} catch (std::exception &e) {
6261
GVA_ERROR("Error during creation writepipe fpscounter: %s", Utils::createNestedErrorMsg(e).c_str());
@@ -65,18 +64,17 @@ void fps_counter_create_writepipe(const char *pipe_name) {
6564

6665
void fps_counter_create_readpipe(void *fpscounter, const char *pipe_name) {
6766
try {
67+
std::lock_guard<std::mutex> lock(channels_mutex);
6868
if (not fps_counters.count("readpipe")) {
6969
auto pipe_complete_lambda = [=]() {
7070
fps_counter_eos(GST_ELEMENT_NAME(GST_BASE_TRANSFORM(fpscounter)));
71-
// Pushing an EOS event downstream to signal that we're done.
7271
bool handled = gst_pad_push_event(GST_BASE_TRANSFORM(fpscounter)->srcpad, gst_event_new_eos());
7372
if (!handled)
7473
throw std::runtime_error("FpsCounter ReadPipe: EOS event wasn't handled. Spinning...");
7574
};
7675
auto new_message_lambda = [](const char *name) { fps_counter_new_frame(NULL, name, NULL); };
77-
std::shared_ptr<FpsCounter> fps_counter = nullptr;
78-
fps_counter = std::make_shared<ReadPipeFpsCounter>(pipe_name, new_message_lambda, pipe_complete_lambda);
79-
fps_counters.insert({"readpipe", fps_counter});
76+
fps_counters.insert({"readpipe",
77+
std::make_shared<ReadPipeFpsCounter>(pipe_name, new_message_lambda, pipe_complete_lambda)});
8078
}
8179
} catch (std::exception &e) {
8280
GVA_ERROR("Error during creation readpipe fpscounter: %s", Utils::createNestedErrorMsg(e).c_str());
@@ -85,6 +83,7 @@ void fps_counter_create_readpipe(void *fpscounter, const char *pipe_name) {
8583

8684
void fps_counter_new_frame(GstBuffer *buffer, const char *element_name, void *gstgvafpscounter) {
8785
try {
86+
std::lock_guard<std::mutex> lock(channels_mutex);
8887
for (auto counter = fps_counters.begin(); counter != fps_counters.end(); ++counter) {
8988
counter->second->NewFrame(element_name, output, buffer);
9089
if (gstgvafpscounter && counter->first == "average") {
@@ -102,6 +101,7 @@ void fps_counter_new_frame(GstBuffer *buffer, const char *element_name, void *gs
102101

103102
void fps_counter_eos(const char *element_name) {
104103
try {
104+
std::lock_guard<std::mutex> lock(channels_mutex);
105105
for (auto counter = fps_counters.begin(); counter != fps_counters.end(); ++counter)
106106
counter->second->EOS(element_name, output);
107107
} catch (std::exception &e) {

src/monolithic/inference_backend/image_inference/openvino/openvino_image_inference.cpp

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
#include <functional>
4444
#include <iterator>
45+
#include <mutex>
4546
#include <regex>
4647
#include <stdio.h>
4748
#include <thread>
@@ -754,12 +755,18 @@ class OpenVinoNewApiImpl {
754755
return 0;
755756
}
756757

757-
// Singleton core object
758758
static ov::Core &core() {
759759
static ov::Core ovcore;
760760
return ovcore;
761761
}
762762

763+
// Serializes compile_model / create_context calls that trigger device plugin init.
764+
// read_model is intentionally NOT serialized — it is a pure XML parse.
765+
static std::mutex &compile_mutex() {
766+
static std::mutex m;
767+
return m;
768+
}
769+
763770
protected:
764771
std::shared_ptr<ov::Model> _model;
765772
std::string _model_path;
@@ -809,10 +816,15 @@ class OpenVinoNewApiImpl {
809816
try {
810817
_batch_size = core().get_property(config.device(), ov::optimal_batch_size);
811818
} catch (...) {
812-
_batch_size = 1; // Fallback if optimal batch size property is not supported
819+
_batch_size = 1;
813820
}
814821
}
815822

823+
if (_device.find("NPU") != std::string::npos && _batch_size > 1) {
824+
GVA_WARNING("NPU does not support batch-size > 1, clamping from %d to 1", _batch_size);
825+
_batch_size = 1;
826+
}
827+
816828
_batch_timeout = config.batch_timeout();
817829

818830
if (_batch_timeout > -1) {
@@ -1071,14 +1083,17 @@ class OpenVinoNewApiImpl {
10711083
}
10721084

10731085
// print_input_and_outputs_info(*_model);
1074-
if (_openvino_context) {
1075-
_compiled_model = core().compile_model(_model, _openvino_context->remote_context(), ov_params);
1076-
} else {
1077-
std::string formatted_device = _device;
1078-
if (_batch_timeout > -1) {
1079-
formatted_device = fmt::format("BATCH:{}({})", _device, _auto_batch_num_requests);
1086+
{
1087+
std::lock_guard<std::mutex> lock(compile_mutex());
1088+
if (_openvino_context) {
1089+
_compiled_model = core().compile_model(_model, _openvino_context->remote_context(), ov_params);
1090+
} else {
1091+
std::string formatted_device = _device;
1092+
if (_batch_timeout > -1) {
1093+
formatted_device = fmt::format("BATCH:{}({})", _device, _auto_batch_num_requests);
1094+
}
1095+
_compiled_model = core().compile_model(_model, formatted_device, ov_params);
10801096
}
1081-
_compiled_model = core().compile_model(_model, formatted_device, ov_params);
10821097
}
10831098
GVA_INFO("Network loaded to device");
10841099

@@ -1129,6 +1144,7 @@ class OpenVinoNewApiImpl {
11291144
if (_app_context) {
11301145
try {
11311146
dlstreamer::D3D11ContextPtr d3d11_ctx = dlstreamer::D3D11Context::create(_app_context);
1147+
std::lock_guard<std::mutex> lock(compile_mutex());
11321148
_openvino_context = std::make_shared<dlstreamer::OpenVINOContext>(core(), _device, d3d11_ctx);
11331149
} catch (std::exception &e) {
11341150
GVA_ERROR("Exception occurred when creating OpenVINO™ toolkit remote context: %s", e.what());
@@ -1144,6 +1160,7 @@ class OpenVinoNewApiImpl {
11441160
if (_app_context) {
11451161
try {
11461162
dlstreamer::VAAPIContextPtr vaapi_ctx = dlstreamer::VAAPIContext::create(_app_context);
1163+
std::lock_guard<std::mutex> lock(compile_mutex());
11471164
_openvino_context = std::make_shared<dlstreamer::OpenVINOContext>(core(), _device, vaapi_ctx);
11481165
} catch (std::exception &e) {
11491166
GVA_ERROR("Exception occurred when creating OpenVINO™ toolkit remote context: %s", e.what());

0 commit comments

Comments
 (0)