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 b52c71e438)
Co-Authored-by: Fable 5
This commit is contained in:
Raja-89
2026-07-23 23:22:32 +05:30
committed by Michael Niedermayer
parent 3c287af3af
commit eebd95977f
6 changed files with 24 additions and 0 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);