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