[vlc-devel] [PATCH v2 5/8] thumbnailer: use vlc_executor_t
Romain Vimont
rom1v at videolabs.io
Sun Sep 13 21:11:18 CEST 2020
Replace the background worker by an executor.
---
src/input/thumbnailer.c | 391 +++++++++++++++++++++++-----------------
1 file changed, 222 insertions(+), 169 deletions(-)
diff --git a/src/input/thumbnailer.c b/src/input/thumbnailer.c
index 783eb2886b..38aa20bfb3 100644
--- a/src/input/thumbnailer.c
+++ b/src/input/thumbnailer.c
@@ -25,49 +25,115 @@
#endif
#include <vlc_thumbnailer.h>
+#include <vlc_executor.h>
#include "input_internal.h"
-#include "misc/background_worker.h"
struct vlc_thumbnailer_t
{
vlc_object_t* parent;
- struct background_worker* worker;
+ vlc_executor_t *executor;
+
+ vlc_mutex_t lock;
+ struct vlc_list submitted_tasks; /**< list of struct thumbnailer_task */
};
-typedef struct vlc_thumbnailer_params_t
+struct seek_target
{
- union
- {
+ enum { VLC_THUMBNAILER_SEEK_TIME, VLC_THUMBNAILER_SEEK_POS } type;
+ union {
vlc_tick_t time;
float pos;
};
- enum
- {
- VLC_THUMBNAILER_SEEK_TIME,
- VLC_THUMBNAILER_SEEK_POS,
- } type;
+};
+
+/* We may not rename vlc_thumbnailer_request_t because it is exposed in the
+ * public API */
+typedef struct vlc_thumbnailer_request_t task_t;
+
+struct vlc_thumbnailer_request_t
+{
+ vlc_thumbnailer_t *thumbnailer;
+
+ struct seek_target seek_target;
bool fast_seek;
- input_item_t* input_item;
+ input_item_t *item;
/**
* A positive value will be used as the timeout duration
* VLC_TICK_INVALID means no timeout
*/
vlc_tick_t timeout;
vlc_thumbnailer_cb cb;
- void* user_data;
-} vlc_thumbnailer_params_t;
+ void* userdata;
-struct vlc_thumbnailer_request_t
-{
- vlc_thumbnailer_t *thumbnailer;
- input_thread_t *input_thread;
+ vlc_mutex_t lock;
+ vlc_cond_t cond_ended;
+ bool ended;
- vlc_thumbnailer_params_t params;
+ struct vlc_runnable runnable; /**< to be passed to the executor */
- vlc_mutex_t lock;
- bool done;
+ struct vlc_list node; /**< node of vlc_thumbnailer_t.submitted_tasks */
};
+static void RunnableRun(void *);
+
+static task_t *
+TaskNew(vlc_thumbnailer_t *thumbnailer, input_item_t *item,
+ struct seek_target seek_target, bool fast_seek,
+ vlc_thumbnailer_cb cb, void *userdata, vlc_tick_t timeout)
+{
+ task_t *task = malloc(sizeof(*task));
+ if (!task)
+ return NULL;
+
+ task->thumbnailer = thumbnailer;
+ task->item = item;
+ task->seek_target = seek_target;
+ task->fast_seek = fast_seek;
+ task->cb = cb;
+ task->userdata = userdata;
+ task->timeout = timeout;
+
+ vlc_mutex_init(&task->lock);
+ vlc_cond_init(&task->cond_ended);
+ task->ended = false;
+
+ task->runnable.run = RunnableRun;
+ task->runnable.userdata = task;
+
+ input_item_Hold(item);
+
+ return task;
+}
+
+static void
+TaskDelete(task_t *task)
+{
+ input_item_Release(task->item);
+ free(task);
+}
+
+static void
+ThumbnailerAddTask(vlc_thumbnailer_t *thumbnailer, task_t *task)
+{
+ vlc_mutex_lock(&thumbnailer->lock);
+ vlc_list_append(&task->node, &thumbnailer->submitted_tasks);
+ vlc_mutex_unlock(&thumbnailer->lock);
+}
+
+static void
+ThumbnailerRemoveTask(vlc_thumbnailer_t *thumbnailer, task_t *task)
+{
+ vlc_mutex_lock(&thumbnailer->lock);
+ vlc_list_remove(&task->node);
+ vlc_mutex_unlock(&thumbnailer->lock);
+}
+
+static void NotifyThumbnail(task_t *task, picture_t *pic)
+{
+ assert(task->cb);
+ task->cb(task->userdata, pic);
+}
+
static void
on_thumbnailer_input_event( input_thread_t *input,
const struct vlc_input_event *event, void *userdata )
@@ -78,181 +144,148 @@ on_thumbnailer_input_event( input_thread_t *input,
event->state.value != END_S ) ) )
return;
- vlc_thumbnailer_request_t* request = userdata;
- picture_t *pic = NULL;
+ task_t *task = userdata;
- if ( event->type == INPUT_EVENT_THUMBNAIL_READY )
- {
- /*
- * Stop the input thread ASAP, delegate its release to
- * thumbnailer_request_Release
- */
- input_Stop( request->input_thread );
- pic = event->thumbnail;
- }
- vlc_mutex_lock( &request->lock );
- request->done = true;
- /*
- * If the request has not been cancelled, we can invoke the completion
- * callback.
- */
- if ( request->params.cb )
+ vlc_mutex_lock(&task->lock);
+ if (task->ended)
{
- request->params.cb( request->params.user_data, pic );
- request->params.cb = NULL;
+ /* We may receive a THUMBNAIL_READY event followed by an
+ * INPUT_EVENT_STATE (end of stream), we must only consider the first
+ * one. */
+ vlc_mutex_unlock(&task->lock);
+ return;
}
- vlc_mutex_unlock( &request->lock );
- background_worker_RequestProbe( request->thumbnailer->worker );
-}
-static void thumbnailer_request_Hold( void* data )
-{
- VLC_UNUSED(data);
-}
+ task->ended = true;
+ vlc_mutex_unlock(&task->lock);
-static void thumbnailer_request_Release( void* data )
-{
- vlc_thumbnailer_request_t* request = data;
- if ( request->input_thread )
- input_Close( request->input_thread );
+ picture_t *pic = NULL;
+ if (event->type == INPUT_EVENT_THUMBNAIL_READY)
+ pic = event->thumbnail;
- input_item_Release( request->params.input_item );
- free( request );
+ NotifyThumbnail(task, pic);
+ vlc_cond_signal(&task->cond_ended);
}
-static int thumbnailer_request_Start( void* owner, void* entity, void** out )
+static void
+RunnableRun(void *userdata)
{
- vlc_thumbnailer_t* thumbnailer = owner;
- vlc_thumbnailer_request_t* request = entity;
- input_thread_t* input = request->input_thread =
- input_CreateThumbnailer( thumbnailer->parent,
- on_thumbnailer_input_event, request,
- request->params.input_item );
- if ( unlikely( input == NULL ) )
+ task_t *task = userdata;
+ vlc_thumbnailer_t *thumbnailer = task->thumbnailer;
+
+ vlc_tick_t now = vlc_tick_now();
+
+ input_thread_t* input =
+ input_CreateThumbnailer(thumbnailer->parent, on_thumbnailer_input_event,
+ task, task->item);
+ if (!input)
+ goto end;
+
+ if (task->seek_target.type == VLC_THUMBNAILER_SEEK_TIME)
+ input_SetTime(input, task->seek_target.time, task->fast_seek);
+ else
{
- request->params.cb( request->params.user_data, NULL );
- return VLC_EGENERIC;
+ assert(task->seek_target.type == VLC_THUMBNAILER_SEEK_POS);
+ input_SetPosition(input, task->seek_target.pos, task->fast_seek);
}
- if ( request->params.type == VLC_THUMBNAILER_SEEK_TIME )
+
+ int ret = input_Start(input);
+ if (ret != VLC_SUCCESS)
{
- input_SetTime( input, request->params.time,
- request->params.fast_seek );
+ input_Close(input);
+ goto end;
}
- else
+
+ vlc_mutex_lock(&task->lock);
+ if (task->timeout == VLC_TICK_INVALID)
{
- assert( request->params.type == VLC_THUMBNAILER_SEEK_POS );
- input_SetPosition( input, request->params.pos,
- request->params.fast_seek );
+ while (!task->ended)
+ vlc_cond_wait(&task->cond_ended, &task->lock);
}
- if ( input_Start( input ) != VLC_SUCCESS )
+ else
{
- request->params.cb( request->params.user_data, NULL );
- return VLC_EGENERIC;
+ vlc_tick_t deadline = now + task->timeout;
+ bool timeout = false;
+ while (!task->ended && !timeout)
+ timeout =
+ vlc_cond_timedwait(&task->cond_ended, &task->lock, deadline);
}
- *out = request;
- return VLC_SUCCESS;
-}
+ vlc_mutex_unlock(&task->lock);
-static void thumbnailer_request_Stop( void* owner, void* handle )
-{
- VLC_UNUSED(owner);
+ input_Stop(input);
+ input_Close(input);
- vlc_thumbnailer_request_t *request = handle;
- vlc_mutex_lock( &request->lock );
- /*
- * If the callback hasn't been invoked yet, we assume a timeout and
- * signal it back to the user
- */
- if ( request->params.cb != NULL )
- {
- request->params.cb( request->params.user_data, NULL );
- request->params.cb = NULL;
- }
- vlc_mutex_unlock( &request->lock );
- assert( request->input_thread != NULL );
- input_Stop( request->input_thread );
+end:
+ ThumbnailerRemoveTask(thumbnailer, task);
+ TaskDelete(task);
}
-static int thumbnailer_request_Probe( void* owner, void* handle )
+static void
+Interrupt(task_t *task)
{
- VLC_UNUSED(owner);
- vlc_thumbnailer_request_t *request = handle;
- vlc_mutex_lock( &request->lock );
- int res = request->done;
- vlc_mutex_unlock( &request->lock );
- return res;
+ /* Wake up RunnableRun() which will call input_Stop() */
+ vlc_mutex_lock(&task->lock);
+ task->ended = true;
+ vlc_mutex_unlock(&task->lock);
+ vlc_cond_signal(&task->cond_ended);
}
-static vlc_thumbnailer_request_t*
-thumbnailer_RequestCommon( vlc_thumbnailer_t* thumbnailer,
- const vlc_thumbnailer_params_t* params )
+static task_t *
+RequestCommon(vlc_thumbnailer_t *thumbnailer, struct seek_target seek_target,
+ enum vlc_thumbnailer_seek_speed speed, input_item_t *item,
+ vlc_tick_t timeout, vlc_thumbnailer_cb cb, void *userdata)
{
- vlc_thumbnailer_request_t *request = malloc( sizeof( *request ) );
- if ( unlikely( request == NULL ) )
- return NULL;
- request->thumbnailer = thumbnailer;
- request->input_thread = NULL;
- request->params = *(vlc_thumbnailer_params_t*)params;
- request->done = false;
- input_item_Hold( request->params.input_item );
- vlc_mutex_init( &request->lock );
-
- int timeout = params->timeout == VLC_TICK_INVALID ?
- 0 : MS_FROM_VLC_TICK( params->timeout );
- if ( background_worker_Push( thumbnailer->worker, request, request,
- timeout ) != VLC_SUCCESS )
- {
- thumbnailer_request_Release( request );
+ bool fast_seek = speed == VLC_THUMBNAILER_SEEK_FAST;
+ task_t *task = TaskNew(thumbnailer, item, seek_target, fast_seek, cb,
+ userdata, timeout);
+ if (!task)
return NULL;
- }
- return request;
+
+ ThumbnailerAddTask(thumbnailer, task);
+
+ vlc_executor_Submit(thumbnailer->executor, &task->runnable);
+
+ /* XXX In theory, "task" might already be invalid here (if it is already
+ * executed and deleted). This is consistent with the API documentation and
+ * the previous implementation, but it is not very convenient for the user.
+ */
+ return task;
}
-vlc_thumbnailer_request_t*
+task_t *
vlc_thumbnailer_RequestByTime( vlc_thumbnailer_t *thumbnailer,
vlc_tick_t time,
enum vlc_thumbnailer_seek_speed speed,
- input_item_t *input_item, vlc_tick_t timeout,
- vlc_thumbnailer_cb cb, void* user_data )
+ input_item_t *item, vlc_tick_t timeout,
+ vlc_thumbnailer_cb cb, void* userdata )
{
- return thumbnailer_RequestCommon( thumbnailer,
- &(const vlc_thumbnailer_params_t){
- .time = time,
- .type = VLC_THUMBNAILER_SEEK_TIME,
- .fast_seek = speed == VLC_THUMBNAILER_SEEK_FAST,
- .input_item = input_item,
- .timeout = timeout,
- .cb = cb,
- .user_data = user_data,
- });
+ struct seek_target seek_target = {
+ .type = VLC_THUMBNAILER_SEEK_TIME,
+ .time = time,
+ };
+ return RequestCommon(thumbnailer, seek_target, speed, item, timeout, cb,
+ userdata);
}
-vlc_thumbnailer_request_t*
+task_t *
vlc_thumbnailer_RequestByPos( vlc_thumbnailer_t *thumbnailer,
float pos, enum vlc_thumbnailer_seek_speed speed,
- input_item_t *input_item, vlc_tick_t timeout,
- vlc_thumbnailer_cb cb, void* user_data )
+ input_item_t *item, vlc_tick_t timeout,
+ vlc_thumbnailer_cb cb, void* userdata )
{
- return thumbnailer_RequestCommon( thumbnailer,
- &(const vlc_thumbnailer_params_t){
- .pos = pos,
- .type = VLC_THUMBNAILER_SEEK_POS,
- .fast_seek = speed == VLC_THUMBNAILER_SEEK_FAST,
- .input_item = input_item,
- .timeout = timeout,
- .cb = cb,
- .user_data = user_data,
- });
+ struct seek_target seek_target = {
+ .type = VLC_THUMBNAILER_SEEK_POS,
+ .time = pos,
+ };
+ return RequestCommon(thumbnailer, seek_target, speed, item, timeout, cb,
+ userdata);
}
-void vlc_thumbnailer_Cancel( vlc_thumbnailer_t* thumbnailer,
- vlc_thumbnailer_request_t* req )
+void vlc_thumbnailer_Cancel( vlc_thumbnailer_t* thumbnailer, task_t* task )
{
- vlc_mutex_lock( &req->lock );
- /* Ensure we won't invoke the callback if the input was running. */
- req->params.cb = NULL;
- vlc_mutex_unlock( &req->lock );
- background_worker_Cancel( thumbnailer->worker, req );
+ (void) thumbnailer;
+ /* The API documentation requires that task is valid */
+ Interrupt(task);
}
vlc_thumbnailer_t *vlc_thumbnailer_Create( vlc_object_t* parent)
@@ -260,27 +293,47 @@ vlc_thumbnailer_t *vlc_thumbnailer_Create( vlc_object_t* parent)
vlc_thumbnailer_t *thumbnailer = malloc( sizeof( *thumbnailer ) );
if ( unlikely( thumbnailer == NULL ) )
return NULL;
- thumbnailer->parent = parent;
- struct background_worker_config cfg = {
- .default_timeout = -1,
- .max_threads = 1,
- .pf_release = thumbnailer_request_Release,
- .pf_hold = thumbnailer_request_Hold,
- .pf_start = thumbnailer_request_Start,
- .pf_probe = thumbnailer_request_Probe,
- .pf_stop = thumbnailer_request_Stop,
- };
- thumbnailer->worker = background_worker_New( thumbnailer, &cfg );
- if ( unlikely( thumbnailer->worker == NULL ) )
+
+ thumbnailer->executor = vlc_executor_New(1);
+ if (!thumbnailer->executor)
{
- free( thumbnailer );
+ free(thumbnailer);
return NULL;
}
+
+ thumbnailer->parent = parent;
+ vlc_mutex_init(&thumbnailer->lock);
+ vlc_list_init(&thumbnailer->submitted_tasks);
+
return thumbnailer;
}
+static void
+CancelAllTasks(vlc_thumbnailer_t *thumbnailer)
+{
+ vlc_mutex_lock(&thumbnailer->lock);
+
+ task_t *task;
+ vlc_list_foreach(task, &thumbnailer->submitted_tasks, node)
+ {
+ bool canceled = vlc_executor_Cancel(thumbnailer->executor,
+ &task->runnable);
+ if (canceled)
+ {
+ NotifyThumbnail(task, NULL);
+ vlc_list_remove(&task->node);
+ TaskDelete(task);
+ }
+ /* Otherwise, the task will be finished and destroyed after run() */
+ }
+
+ vlc_mutex_unlock(&thumbnailer->lock);
+}
+
void vlc_thumbnailer_Release( vlc_thumbnailer_t *thumbnailer )
{
- background_worker_Delete( thumbnailer->worker );
+ CancelAllTasks(thumbnailer);
+
+ vlc_executor_Delete(thumbnailer->executor);
free( thumbnailer );
}
--
2.28.0
More information about the vlc-devel
mailing list