vlc | branch: master | Romain Vimont <[email protected]> | Fri Sep 4 13:21:18 2020 +0200| [adfe2249db3cd9fd4b8e568ba3a9dadd9d107768] | committer: Alexandre Janniaux
thumbnailer: use vlc_executor_t Replace the background worker by an executor. Signed-off-by: Alexandre Janniaux <[email protected]> > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=adfe2249db3cd9fd4b8e568ba3a9dadd9d107768 --- src/input/thumbnailer.c | 392 +++++++++++++++++++++++++++--------------------- 1 file changed, 225 insertions(+), 167 deletions(-) diff --git a/src/input/thumbnailer.c b/src/input/thumbnailer.c index 783eb2886b..c063cfecf4 100644 --- a/src/input/thumbnailer.c +++ b/src/input/thumbnailer.c @@ -25,49 +25,120 @@ #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 - { - vlc_tick_t time; - float pos; - }; enum { VLC_THUMBNAILER_SEEK_TIME, VLC_THUMBNAILER_SEEK_POS, } type; + union + { + vlc_tick_t time; + float pos; + }; +}; + +/* 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 +149,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 ) + vlc_mutex_lock(&task->lock); + if (task->ended) { - /* - * 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 ) - { - 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 ) ) + 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; - 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 ); - 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 +298,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 ); } _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
