<html><head></head><body>Hello,<br>
<br>
Any default limit larger than one will, with large enough user base, cause failures and performance problems. Of course, a default limit of one makes the feature mostly useless. A rock and a hard place...<br>
<br>
However, the first problem is the lack of timeout IIRC - not the lack of parallelism. Spawn however many threads, and without timeout, you might not make any progress anyway.<br><br><div class="gmail_quote">Le 17 juillet 2018 14:24:56 GMT+03:00, Romain Vimont <rom1v@videolabs.io> a écrit :<blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<pre class="k9mail">A way to speed up the preparsing consists in preparsing several inputs<br>in parallel.<br><br>For this purpose, make the background worker (used by the preparser and<br>fetcher) execute tasks from (possibly) several threads.<br><br>Apart from adding a new field "max_threads" in the<br>background_worker_config structure, the background worker API is kept<br>unchanged.<br><br>Two new options are added to configure the maximum number of threads<br>used for preparsing and fetching:<br> - preparse-threads<br> - fetch-art-threads<br>---<br>This patch depends on:<br> input: preparser: prepare for multithreaded background worker<br> <https://mailman.videolan.org/pipermail/vlc-devel/2018-July/120394.html><br><br> src/libvlc-module.c | 14 ++<br> src/misc/background_worker.c | 435 +++++++++++++++++++++--------------<br> src/misc/background_worker.h | 20 ++<br> src/preparser/fetcher.c | 1 +<br> src/preparser/preparser.c | 1 +<br> 5 files changed, 303 insertions(+), 168 deletions(-)<br><br>diff --git a/src/libvlc-module.c b/src/libvlc-module.c<br>index 82862ab4745..cac63239c65 100644<br>--- a/src/libvlc-module.c<br>+++ b/src/libvlc-module.c<br>@@ -1125,6 +1125,14 @@ static const char *const ppsz_prefres[] = {<br> #define PREPARSE_TIMEOUT_LONGTEXT N_( \<br> "Maximum time allowed to preparse an item, in milliseconds" )<br> <br>+#define PREPARSE_THREADS_TEXT N_( "Preparsing threads" )<br>+#define PREPARSE_THREADS_LONGTEXT N_( \<br>+ "Maximum number of threads used to preparse items" )<br>+<br>+#define FETCH_ART_THREADS_TEXT N_( "Fetch-art threads" )<br>+#define FETCH_ART_THREADS_LONGTEXT N_( \<br>+ "Maximum number of threads used to fetch art" )<br>+<br> #define METADATA_NETWORK_TEXT N_( "Allow metadata network access" )<br> <br> static const char *const psz_recursive_list[] = {<br>@@ -2110,6 +2118,12 @@ vlc_module_begin ()<br> add_integer( "preparse-timeout", 5000, PREPARSE_TIMEOUT_TEXT,<br> PREPARSE_TIMEOUT_LONGTEXT, false )<br> <br>+ add_integer( "preparse-threads", 2, PREPARSE_THREADS_TEXT,<br>+ PREPARSE_THREADS_LONGTEXT, false )<br>+<br>+ add_integer( "fetch-art-threads", 2, FETCH_ART_THREADS_TEXT,<br>+ FETCH_ART_THREADS_LONGTEXT, false )<br>+<br> add_obsolete_integer( "album-art" )<br> add_bool( "metadata-network-access", false, METADATA_NETWORK_TEXT,<br> METADATA_NETWORK_TEXT, false )<br>diff --git a/src/misc/background_worker.c b/src/misc/background_worker.c<br>index 2f21e5006a3..1f2796e5d39 100644<br>--- a/src/misc/background_worker.c<br>+++ b/src/misc/background_worker.c<br>@@ -22,246 +22,345 @@<br> <br> #include <assert.h><br> #include <vlc_common.h><br>+#include <vlc_atomic.h><br>+#include <vlc_list.h><br> #include <vlc_threads.h><br>-#include <vlc_arrays.h><br> <br> #include "libvlc.h"<br> #include "background_worker.h"<br> <br>-struct bg_queued_item {<br>+struct task {<br>+ struct vlc_list node;<br> void* id; /**< id associated with entity */<br> void* entity; /**< the entity to process */<br>- int timeout; /**< timeout duration in microseconds */<br>+ int timeout; /**< timeout duration in milliseconds */<br>+};<br>+<br>+struct background_worker;<br>+<br>+struct background_thread {<br>+ struct background_worker *owner;<br>+ vlc_cond_t probe_cancel_wait; /**< wait for probe request or cancelation */<br>+ bool probe; /**< true if a probe is requested */<br>+ bool cancel; /**< true if a cancel is requested */<br>+ struct task *task; /**< current task */<br>+ struct vlc_list node;<br> };<br> <br> struct background_worker {<br> void* owner;<br> struct background_worker_config conf;<br> <br>- vlc_mutex_t lock; /**< acquire to inspect members that follow */<br>- struct {<br>- bool probe_request; /**< true if a probe is requested */<br>- vlc_cond_t wait; /**< wait for update in terms of head */<br>- vlc_cond_t worker_wait; /**< wait for probe request or cancelation */<br>- vlc_tick_t deadline; /**< deadline of the current task */<br>- void* id; /**< id of the current task */<br>- bool active; /**< true if there is an active thread */<br>- } head;<br>+ vlc_mutex_t lock;<br> <br>- struct {<br>- vlc_cond_t wait; /**< wait for update in terms of tail */<br>- vlc_array_t data; /**< queue of pending entities to process */<br>- } tail;<br>+ int uncompleted; /**< number of tasks requested but not completed */<br>+ int nthreads; /**< number of threads in the threads list */<br>+ struct vlc_list threads; /**< list of active background_thread instances */<br>+<br>+ struct vlc_list queue; /**< queue of tasks */<br>+ vlc_cond_t queue_wait; /**< wait for the queue to be non-empty */<br>+<br>+ vlc_cond_t nothreads_wait; /**< wait for nthreads == 0 */<br>+ bool closing; /**< true if background worker deletion is requested */<br> };<br> <br>+static struct task *task_Create(struct background_worker *worker, void *id,<br>+ void *entity, int timeout)<br>+{<br>+ struct task *task = malloc(sizeof(*task));<br>+ if (unlikely(!task))<br>+ return NULL;<br>+<br>+ task->id = id;<br>+ task->entity = entity;<br>+ task->timeout = timeout < 0 ? worker->conf.default_timeout : timeout;<br>+ worker->conf.pf_hold(task->entity);<br>+ return task;<br>+}<br>+<br>+static void task_Destroy(struct background_worker *worker, struct task *task)<br>+{<br>+ worker->conf.pf_release(task->entity);<br>+ free(task);<br>+}<br>+<br>+static struct task *QueueTake(struct background_worker *worker, int timeout_ms)<br>+{<br>+ vlc_assert_locked(&worker->lock);<br>+<br>+ vlc_tick_t deadline = vlc_tick_now() + VLC_TICK_FROM_MS(timeout_ms);<br>+ bool timeout = false;<br>+ while (!timeout && !worker->closing && vlc_list_is_empty(&worker->queue))<br>+ timeout = vlc_cond_timedwait(&worker->queue_wait,<br>+ &worker->lock, deadline) != 0;<br>+<br>+ if (worker->closing || timeout)<br>+ return NULL;<br>+<br>+ struct task *task = vlc_list_first_entry_or_null(&worker->queue,<br>+ struct task, node);<br>+ assert(task);<br>+ vlc_list_remove(&task->node);<br>+<br>+ return task;<br>+}<br>+<br>+static void QueuePush(struct background_worker *worker, struct task *task)<br>+{<br>+ vlc_assert_locked(&worker->lock);<br>+ vlc_list_append(&task->node, &worker->queue);<br>+ vlc_cond_signal(&worker->queue_wait);<br>+}<br>+<br>+static void QueueRemoveAll(struct background_worker *worker, void *id)<br>+{<br>+ vlc_assert_locked(&worker->lock);<br>+ struct task *task;<br>+ vlc_list_foreach(task, &worker->queue, node)<br>+ {<br>+ if (!id || task->id == id)<br>+ {<br>+ vlc_list_remove(&task->node);<br>+ task_Destroy(worker, task);<br>+ }<br>+ }<br>+}<br>+<br>+static struct background_thread *<br>+background_thread_Create(struct background_worker *owner)<br>+{<br>+ struct background_thread *thread = malloc(sizeof(*thread));<br>+ if (!thread)<br>+ return NULL;<br>+<br>+ vlc_cond_init(&thread->probe_cancel_wait);<br>+ thread->probe = false;<br>+ thread->cancel = false;<br>+ thread->task = NULL;<br>+ thread->owner = owner;<br>+ return thread;<br>+}<br>+<br>+static void background_thread_Destroy(struct background_thread *thread)<br>+{<br>+ vlc_cond_destroy(&thread->probe_cancel_wait);<br>+ free(thread);<br>+}<br>+<br>+static struct background_worker *background_worker_Create(void *owner,<br>+ struct background_worker_config *conf)<br>+{<br>+ struct background_worker* worker = malloc(sizeof(*worker));<br>+ if (unlikely(!worker))<br>+ return NULL;<br>+<br>+ worker->conf = *conf;<br>+ worker->owner = owner;<br>+<br>+ vlc_mutex_init(&worker->lock);<br>+ worker->uncompleted = 0;<br>+ worker->nthreads = 0;<br>+ vlc_list_init(&worker->threads);<br>+ vlc_list_init(&worker->queue);<br>+ vlc_cond_init(&worker->queue_wait);<br>+ vlc_cond_init(&worker->nothreads_wait);<br>+ worker->closing = false;<br>+ return worker;<br>+}<br>+<br>+static void background_worker_Destroy(struct background_worker *worker)<br>+{<br>+ vlc_cond_destroy(&worker->queue_wait);<br>+ vlc_mutex_destroy(&worker->lock);<br>+ free(worker);<br>+}<br>+<br>+static void EndTask(struct background_thread *thread, struct task *task)<br>+{<br>+ struct background_worker *worker = thread->owner;<br>+ task_Destroy(worker, task);<br>+<br>+ vlc_mutex_lock(&worker->lock);<br>+ thread->task = NULL;<br>+ worker->uncompleted--;<br>+ assert(worker->uncompleted >= 0);<br>+ vlc_mutex_unlock(&worker->lock);<br>+}<br>+<br>+static void RemoveThread(struct background_thread *thread)<br>+{<br>+ struct background_worker *worker = thread->owner;<br>+<br>+ vlc_mutex_lock(&worker->lock);<br>+<br>+ vlc_list_remove(&thread->node);<br>+ worker->nthreads--;<br>+ assert(worker->nthreads >= 0);<br>+ if (!worker->nthreads)<br>+ vlc_cond_signal(&worker->nothreads_wait);<br>+<br>+ vlc_mutex_unlock(&worker->lock);<br>+<br>+ background_thread_Destroy(thread);<br>+}<br>+<br> static void* Thread( void* data )<br> {<br>- struct background_worker* worker = data;<br>+ struct background_thread *thread = data;<br>+ struct background_worker *worker = thread->owner;<br> <br>- for( ;; )<br>+ for (;;)<br> {<br>- struct bg_queued_item* item = NULL;<br>- void* handle;<br>-<br>- vlc_mutex_lock( &worker->lock );<br>- for( ;; )<br>- {<br>- if( vlc_array_count( &worker->tail.data ) )<br>- {<br>- item = vlc_array_item_at_index( &worker->tail.data, 0 );<br>- handle = NULL;<br>-<br>- vlc_array_remove( &worker->tail.data, 0 );<br>- }<br>-<br>- if( worker->head.deadline == VLC_TICK_INVALID && item == NULL )<br>- worker->head.active = false;<br>- worker->head.id = item ? item->id : NULL;<br>- vlc_cond_broadcast( &worker->head.wait );<br>-<br>- if( item )<br>- {<br>- if( item->timeout > 0 )<br>- worker->head.deadline = vlc_tick_now() + item->timeout * 1000;<br>- else<br>- worker->head.deadline = INT64_MAX;<br>- }<br>- else if( worker->head.deadline != VLC_TICK_INVALID )<br>- {<br>- /* Wait 1 seconds for new inputs before terminating */<br>- vlc_tick_t deadline = vlc_tick_now() + VLC_TICK_FROM_SEC(1);<br>- int ret = vlc_cond_timedwait( &worker->tail.wait,<br>- &worker->lock, deadline );<br>- if( ret != 0 )<br>- {<br>- /* Timeout: if there is still no items, the thread will be<br>- * terminated at next loop iteration (active = false). */<br>- worker->head.deadline = VLC_TICK_INVALID;<br>- }<br>- continue;<br>- }<br>- break;<br>- }<br>-<br>- if( !worker->head.active )<br>+ vlc_mutex_lock(&worker->lock);<br>+ struct task *task = QueueTake(worker, 5000);<br>+ if (!task)<br> {<br>- vlc_mutex_unlock( &worker->lock );<br>+ vlc_mutex_unlock(&worker->lock);<br>+ /* terminate this thread */<br> break;<br> }<br>- vlc_mutex_unlock( &worker->lock );<br> <br>- assert( item != NULL );<br>+ thread->task = task;<br>+ thread->cancel = false;<br>+ thread->probe = false;<br>+ vlc_tick_t deadline;<br>+ if (task->timeout > 0)<br>+ deadline = vlc_tick_now() + VLC_TICK_FROM_MS(task->timeout);<br>+ else<br>+ deadline = INT64_MAX; /* no deadline */<br>+ vlc_mutex_unlock(&worker->lock);<br> <br>- if( worker->conf.pf_start( worker->owner, item->entity, &handle ) )<br>+ void *handle;<br>+ if (worker->conf.pf_start(worker->owner, task->entity, &handle))<br> {<br>- worker->conf.pf_release( item->entity );<br>- free( item );<br>+ EndTask(thread, task);<br> continue;<br> }<br> <br>- for( ;; )<br>+ for (;;)<br> {<br>- vlc_mutex_lock( &worker->lock );<br>+ vlc_mutex_lock(&worker->lock);<br>+ bool timeout = false;<br>+ while (!timeout && !thread->probe && !thread->cancel)<br>+ /* any non-zero return value means timeout */<br>+ timeout = vlc_cond_timedwait(&thread->probe_cancel_wait,<br>+ &worker->lock, deadline) != 0;<br> <br>- bool const b_timeout = worker->head.deadline <= vlc_tick_now();<br>- worker->head.probe_request = false;<br>+ bool cancel = thread->cancel;<br>+ thread->cancel = false;<br>+ thread->probe = false;<br>+ vlc_mutex_unlock(&worker->lock);<br> <br>- vlc_mutex_unlock( &worker->lock );<br>-<br>- if( b_timeout ||<br>- worker->conf.pf_probe( worker->owner, handle ) )<br>+ if (timeout || cancel<br>+ || worker->conf.pf_probe(worker->owner, handle))<br> {<br>- worker->conf.pf_stop( worker->owner, handle );<br>- worker->conf.pf_release( item->entity );<br>- free( item );<br>+ worker->conf.pf_stop(worker->owner, handle);<br>+ EndTask(thread, task);<br> break;<br> }<br>-<br>- vlc_mutex_lock( &worker->lock );<br>- if( worker->head.probe_request == false &&<br>- worker->head.deadline > vlc_tick_now() )<br>- {<br>- vlc_cond_timedwait( &worker->head.worker_wait, &worker->lock,<br>- worker->head.deadline );<br>- }<br>- vlc_mutex_unlock( &worker->lock );<br> }<br> }<br> <br>+ RemoveThread(thread);<br>+<br> return NULL;<br> }<br> <br>-static void BackgroundWorkerCancel( struct background_worker* worker, void* id)<br>+static bool SpawnThread(struct background_worker *worker)<br> {<br>- vlc_mutex_lock( &worker->lock );<br>- for( size_t i = 0; i < vlc_array_count( &worker->tail.data ); )<br>- {<br>- struct bg_queued_item* item =<br>- vlc_array_item_at_index( &worker->tail.data, i );<br>-<br>- if( id == NULL || item->id == id )<br>- {<br>- vlc_array_remove( &worker->tail.data, i );<br>- worker->conf.pf_release( item->entity );<br>- free( item );<br>- continue;<br>- }<br>+ vlc_assert_locked(&worker->lock);<br> <br>- ++i;<br>- }<br>+ struct background_thread *thread = background_thread_Create(worker);<br>+ if (!thread)<br>+ return false;<br> <br>- while( ( id == NULL && worker->head.active )<br>- || ( id != NULL && worker->head.id == id ) )<br>+ if (vlc_clone_detach(NULL, Thread, thread, VLC_THREAD_PRIORITY_LOW))<br> {<br>- worker->head.deadline = VLC_TICK_INVALID;<br>- vlc_cond_signal( &worker->head.worker_wait );<br>- vlc_cond_signal( &worker->tail.wait );<br>- vlc_cond_wait( &worker->head.wait, &worker->lock );<br>+ free(thread);<br>+ return false;<br> }<br>- vlc_mutex_unlock( &worker->lock );<br>+ worker->nthreads++;<br>+ vlc_list_append(&thread->node, &worker->threads);<br>+<br>+ return true;<br> }<br> <br> struct background_worker* background_worker_New( void* owner,<br> struct background_worker_config* conf )<br> {<br>- struct background_worker* worker = malloc( sizeof *worker );<br>-<br>- if( unlikely( !worker ) )<br>- return NULL;<br>-<br>- worker->conf = *conf;<br>- worker->owner = owner;<br>- worker->head.id = NULL;<br>- worker->head.active = false;<br>- worker->head.deadline = VLC_TICK_INVALID;<br>-<br>- vlc_mutex_init( &worker->lock );<br>- vlc_cond_init( &worker->head.wait );<br>- vlc_cond_init( &worker->head.worker_wait );<br>-<br>- vlc_array_init( &worker->tail.data );<br>- vlc_cond_init( &worker->tail.wait );<br>-<br>- return worker;<br>+ return background_worker_Create(owner, conf);<br> }<br> <br> int background_worker_Push( struct background_worker* worker, void* entity,<br> void* id, int timeout )<br> {<br>- struct bg_queued_item* item = malloc( sizeof( *item ) );<br>+ struct task *task = task_Create(worker, id, entity, timeout);<br>+ if (unlikely(!task))<br>+ return VLC_ENOMEM;<br> <br>- if( unlikely( !item ) )<br>- return VLC_EGENERIC;<br>+ vlc_mutex_lock(&worker->lock);<br>+ QueuePush(worker, task);<br>+ if (++worker->uncompleted > worker->nthreads<br>+ && worker->nthreads < worker->conf.max_threads)<br>+ SpawnThread(worker);<br>+ vlc_mutex_unlock(&worker->lock);<br> <br>- item->id = id;<br>- item->entity = entity;<br>- item->timeout = timeout < 0 ? worker->conf.default_timeout : timeout;<br>+ return VLC_SUCCESS;<br>+}<br> <br>- vlc_mutex_lock( &worker->lock );<br>- int i_ret = vlc_array_append( &worker->tail.data, item );<br>- vlc_cond_signal( &worker->tail.wait );<br>- if( i_ret != 0 )<br>- {<br>- free( item );<br>- return VLC_EGENERIC;<br>- }<br>+static void BackgroundWorkerCancelLocked(struct background_worker *worker,<br>+ void *id)<br>+{<br>+ vlc_assert_locked(&worker->lock);<br>+<br>+ QueueRemoveAll(worker, id);<br> <br>- if( worker->head.active == false )<br>+ struct background_thread *thread;<br>+ vlc_list_foreach(thread, &worker->threads, node)<br> {<br>- worker->head.probe_request = false;<br>- worker->head.active =<br>- !vlc_clone_detach( NULL, Thread, worker, VLC_THREAD_PRIORITY_LOW );<br>+ if (!id || (thread->task && thread->task->id == id && !thread->cancel))<br>+ {<br>+ thread->cancel = true;<br>+ vlc_cond_signal(&thread->probe_cancel_wait);<br>+ }<br> }<br>-<br>- if( worker->head.active )<br>- worker->conf.pf_hold( item->entity );<br>-<br>- int ret = worker->head.active ? VLC_SUCCESS : VLC_EGENERIC;<br>- vlc_mutex_unlock( &worker->lock );<br>-<br>- return ret;<br> }<br> <br> void background_worker_Cancel( struct background_worker* worker, void* id )<br> {<br>- BackgroundWorkerCancel( worker, id );<br>+ vlc_mutex_lock(&worker->lock);<br>+ BackgroundWorkerCancelLocked(worker, id);<br>+ vlc_mutex_unlock(&worker->lock);<br> }<br> <br> void background_worker_RequestProbe( struct background_worker* worker )<br> {<br>- vlc_mutex_lock( &worker->lock );<br>- worker->head.probe_request = true;<br>- vlc_cond_signal( &worker->head.worker_wait );<br>- vlc_mutex_unlock( &worker->lock );<br>+ vlc_mutex_lock(&worker->lock);<br>+<br>+ struct background_thread *thread;<br>+ vlc_list_foreach(thread, &worker->threads, node)<br>+ {<br>+ thread->probe = true;<br>+ vlc_cond_signal(&thread->probe_cancel_wait);<br>+ }<br>+<br>+ vlc_mutex_unlock(&worker->lock);<br> }<br> <br> void background_worker_Delete( struct background_worker* worker )<br> {<br>- BackgroundWorkerCancel( worker, NULL );<br>- vlc_array_clear( &worker->tail.data );<br>- vlc_mutex_destroy( &worker->lock );<br>- vlc_cond_destroy( &worker->head.wait );<br>- vlc_cond_destroy( &worker->head.worker_wait );<br>- vlc_cond_destroy( &worker->tail.wait );<br>- free( worker );<br>+ vlc_mutex_lock(&worker->lock);<br>+<br>+ worker->closing = true;<br>+ BackgroundWorkerCancelLocked(worker, NULL);<br>+ /* closing is now true, this will wake up any QueueTake() */<br>+ vlc_cond_broadcast(&worker->queue_wait);<br>+<br>+ while (worker->nthreads)<br>+ vlc_cond_wait(&worker->nothreads_wait, &worker->lock);<br>+<br>+ vlc_mutex_unlock(&worker->lock);<br>+<br>+ /* no threads use the worker anymore, we can destroy it */<br>+ background_worker_Destroy(worker);<br> }<br>diff --git a/src/misc/background_worker.h b/src/misc/background_worker.h<br>index 049e47d543d..abe65159ff2 100644<br>--- a/src/misc/background_worker.h<br>+++ b/src/misc/background_worker.h<br>@@ -29,6 +29,11 @@ struct background_worker_config {<br> **/<br> vlc_tick_t default_timeout;<br> <br>+ /**<br>+ * Maximum number of threads used to execute tasks.<br>+ */<br>+ int max_threads;<br>+<br> /**<br> * Release an entity<br> *<br>@@ -37,6 +42,9 @@ struct background_worker_config {<br> * pf_stop has finished executing, or if the entity is removed from the<br> * queue (through \ref background_worker_Cancel)<br> *<br>+ * \warning As each task might be executed in parallel by different threads,<br>+ * this callback must be thread-safe.<br>+ *<br> * \param entity the entity to release<br> **/<br> void( *pf_release )( void* entity );<br>@@ -48,6 +56,9 @@ struct background_worker_config {<br> * entity. It will happen when the entity is pushed into the queue of<br> * pending tasks as part of \ref background_worker_Push.<br> *<br>+ * \warning As each task might be executed in parallel by different threads,<br>+ * this callback must be thread-safe.<br>+ *<br> * \param entity the entity to hold<br> **/<br> void( *pf_hold )( void* entity );<br>@@ -63,6 +74,9 @@ struct background_worker_config {<br> * The value of `*out` will then be the value of the argument named `handle`<br> * in terms of \ref pf_probe and \ref pf_stop.<br> *<br>+ * \warning As each task might be executed in parallel by different threads,<br>+ * this callback must be thread-safe.<br>+ *<br> * \param owner the owner of the background-worker<br> * \param entity the entity for which a task is to be created<br> * \param out [out] `*out` shall, on success, refer to the handle associated<br>@@ -78,6 +92,9 @@ struct background_worker_config {<br> * finished or not. It can be called anytime between a successful call to<br> * \ref pf_start, and the corresponding call to \ref pf_stop.<br> *<br>+ * \warning As each task might be executed in parallel by different threads,<br>+ * this callback must be thread-safe.<br>+ *<br> * \param owner the owner of the background-worker<br> * \param handle the handle associated with the running task<br> * \return 0 if the task is still running, any other value if finished.<br>@@ -95,6 +112,9 @@ struct background_worker_config {<br> * that the task has finished, or if the timeout (if any) for the<br> * task has been reached.<br> *<br>+ * \warning As each task might be executed in parallel by different threads,<br>+ * this callback must be thread-safe.<br>+ *<br> * \param owner the owner of the background-worker<br> * \parma handle the handle associated with the task to be stopped<br> **/<br>diff --git a/src/preparser/fetcher.c b/src/preparser/fetcher.c<br>index 16af948551c..6d095963afb 100644<br>--- a/src/preparser/fetcher.c<br>+++ b/src/preparser/fetcher.c<br>@@ -404,6 +404,7 @@ static void WorkerInit( input_fetcher_t* fetcher,<br> {<br> struct background_worker_config conf = {<br> .default_timeout = 0,<br>+ .max_threads = var_InheritInteger( fetcher->owner, "fetch-art-threads" ),<br> .pf_start = starter,<br> .pf_probe = ProbeWorker,<br> .pf_stop = CloseWorker,<br>diff --git a/src/preparser/preparser.c b/src/preparser/preparser.c<br>index 529464e4b89..1f3833f5bc5 100644<br>--- a/src/preparser/preparser.c<br>+++ b/src/preparser/preparser.c<br>@@ -153,6 +153,7 @@ input_preparser_t* input_preparser_New( vlc_object_t *parent )<br> <br> struct background_worker_config conf = {<br> .default_timeout = var_InheritInteger( parent, "preparse-timeout" ),<br>+ .max_threads = var_InheritInteger( parent, "preparse-threads" ),<br> .pf_start = PreparserOpenInput,<br> .pf_probe = PreparserProbeInput,<br> .pf_stop = PreparserCloseInput,</pre></blockquote></div><br>
-- <br>
Envoyé de mon appareil Android avec Courriel K-9 Mail. Veuillez excuser ma brièveté.</body></html>