From eebd95977f3c78b01dfaf0d863e2bdaa72f84e88 Mon Sep 17 00:00:00 2001 From: Raja-89 Date: Thu, 23 Jul 2026 23:22:32 +0530 Subject: [PATCH] avfilter/dnn: fix async teardown race condition in all backends When the filter graph is torn down early (e.g. at EOF with -frames:v), the main thread calls dnn_free_model and destroys the request queue while async inference threads may still be running. When a detached thread finishes and tries to push its result back to the destroyed queue, it triggers a segmentation fault. Add ff_dnn_wait_requests() to dnn_backend_common which blocks until all allocated request items (ctx->nireq) have been safely returned to the request_queue. Call it from dnn_free_model in the torch, tensorflow, openvino, and onnx backends before destroying the queue. (cherry picked from commit b52c71e43813952650a74c57b2f31bd0166600ba) Co-Authored-by: Fable 5 --- libavfilter/dnn/dnn_backend_common.c | 9 +++++++++ libavfilter/dnn/dnn_backend_common.h | 11 +++++++++++ libavfilter/dnn/dnn_backend_onnx.c | 1 + libavfilter/dnn/dnn_backend_openvino.c | 1 + libavfilter/dnn/dnn_backend_tf.c | 1 + libavfilter/dnn/dnn_backend_torch.cpp | 1 + 6 files changed, 24 insertions(+) diff --git a/libavfilter/dnn/dnn_backend_common.c b/libavfilter/dnn/dnn_backend_common.c index e45eefd14d..4d6752ae33 100644 --- a/libavfilter/dnn/dnn_backend_common.c +++ b/libavfilter/dnn/dnn_backend_common.c @@ -22,6 +22,7 @@ */ #include "libavutil/mem.h" +#include "libavutil/time.h" #include "dnn_backend_common.h" #define DNN_ASYNC_SUCCESS (void *)0 @@ -102,6 +103,14 @@ int ff_dnn_async_module_cleanup(DNNAsyncExecModule *async_module) return 0; } +void ff_dnn_wait_requests(SafeQueue *request_queue, int nireq) +{ + if (!request_queue) + return; + while (ff_safe_queue_size(request_queue) < nireq) + av_usleep(10000); +} + int ff_dnn_start_inference_async(void *ctx, DNNAsyncExecModule *async_module) { int ret; diff --git a/libavfilter/dnn/dnn_backend_common.h b/libavfilter/dnn/dnn_backend_common.h index 9f5d37b3e0..f0b8b47397 100644 --- a/libavfilter/dnn/dnn_backend_common.h +++ b/libavfilter/dnn/dnn_backend_common.h @@ -25,6 +25,7 @@ #define AVFILTER_DNN_DNN_BACKEND_COMMON_H #include "queue.h" +#include "safe_queue.h" #include "../dnn_interface.h" #include "libavutil/thread.h" @@ -112,6 +113,16 @@ int ff_dnn_fill_task(TaskItem *task, DNNExecBaseParams *exec_params, void *backe */ int ff_dnn_async_module_cleanup(DNNAsyncExecModule *async_module); +/** + * Wait for all inference requests to complete before teardown. + * This blocks the calling thread until all request items have been + * returned to the request_queue by the async inference threads. + * + * @param request_queue pointer to the SafeQueue holding request items + * @param nireq total number of allocated request items + */ +void ff_dnn_wait_requests(SafeQueue *request_queue, int nireq); + /** * Start asynchronous inference routine for the TensorFlow * model on a detached thread. It calls the completion callback diff --git a/libavfilter/dnn/dnn_backend_onnx.c b/libavfilter/dnn/dnn_backend_onnx.c index 0ff0ffb285..6c75d6eb24 100644 --- a/libavfilter/dnn/dnn_backend_onnx.c +++ b/libavfilter/dnn/dnn_backend_onnx.c @@ -152,6 +152,7 @@ static void dnn_free_model_onnx(DNNModel **model) onnx_model = (ONNXModel *)(*model); + ff_dnn_wait_requests(onnx_model->request_queue, onnx_model->ctx->nireq); while (ff_safe_queue_size(onnx_model->request_queue) != 0) { ONNXRequestItem *item = (ONNXRequestItem *)ff_safe_queue_pop_front(onnx_model->request_queue); destroy_request_item(&item); diff --git a/libavfilter/dnn/dnn_backend_openvino.c b/libavfilter/dnn/dnn_backend_openvino.c index ec46e29b8a..764e8dabb5 100644 --- a/libavfilter/dnn/dnn_backend_openvino.c +++ b/libavfilter/dnn/dnn_backend_openvino.c @@ -517,6 +517,7 @@ static void dnn_free_model_ov(DNNModel **model) return; ov_model = (OVModel *)(*model); + ff_dnn_wait_requests(ov_model->request_queue, ov_model->ctx->nireq); while (ff_safe_queue_size(ov_model->request_queue) != 0) { OVRequestItem *item = ff_safe_queue_pop_front(ov_model->request_queue); if (item && item->infer_request) { diff --git a/libavfilter/dnn/dnn_backend_tf.c b/libavfilter/dnn/dnn_backend_tf.c index 48372bea38..07129bcdb6 100644 --- a/libavfilter/dnn/dnn_backend_tf.c +++ b/libavfilter/dnn/dnn_backend_tf.c @@ -486,6 +486,7 @@ static void dnn_free_model_tf(DNNModel **model) return; tf_model = (TFModel *)(*model); + ff_dnn_wait_requests(tf_model->request_queue, tf_model->ctx->nireq); while (ff_safe_queue_size(tf_model->request_queue) != 0) { TFRequestItem *item = ff_safe_queue_pop_front(tf_model->request_queue); destroy_request_item(&item); diff --git a/libavfilter/dnn/dnn_backend_torch.cpp b/libavfilter/dnn/dnn_backend_torch.cpp index 24a202f493..5f16234b8e 100644 --- a/libavfilter/dnn/dnn_backend_torch.cpp +++ b/libavfilter/dnn/dnn_backend_torch.cpp @@ -121,6 +121,7 @@ static void dnn_free_model_th(DNNModel **model) th_model = (THModel *)(*model); if (th_model->request_queue) { + ff_dnn_wait_requests(th_model->request_queue, th_model->ctx->nireq); while (ff_safe_queue_size(th_model->request_queue) != 0) { THRequestItem *item = (THRequestItem *)ff_safe_queue_pop_front(th_model->request_queue); destroy_request_item(&item);