preparser: use vlc_executor_t
[vlc.git] / src / preparser / preparser.c
blobe5bd0b87cb8b223a0115bc23eedcd562255ebf39
1 /*****************************************************************************
2 * preparser.c
3 *****************************************************************************
4 * Copyright © 2017-2017 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <vlc_common.h>
26 #include <vlc_atomic.h>
27 #include <vlc_executor.h>
29 #include "input/input_interface.h"
30 #include "input/input_internal.h"
31 #include "preparser.h"
32 #include "fetcher.h"
34 struct input_preparser_t
36 vlc_object_t* owner;
37 input_fetcher_t* fetcher;
38 vlc_executor_t *executor;
39 vlc_tick_t default_timeout;
40 atomic_bool deactivated;
42 vlc_mutex_t lock;
43 struct vlc_list submitted_tasks; /**< list of struct task */
46 struct task
48 input_preparser_t *preparser;
49 input_item_t *item;
50 input_item_meta_request_option_t options;
51 const input_preparser_callbacks_t *cbs;
52 void *userdata;
53 void *id;
54 vlc_tick_t timeout;
56 input_item_parser_id_t *parser;
58 vlc_mutex_t lock;
59 vlc_cond_t cond_ended;
60 bool preparse_ended;
61 int preparse_status;
62 bool fetch_ended;
64 atomic_bool interrupted;
66 struct vlc_runnable runnable; /**< to be passed to the executor */
68 struct vlc_list node; /**< node of input_preparser_t.submitted_tasks */
71 static void RunnableRun(void *);
73 static struct task *
74 TaskNew(input_preparser_t *preparser, input_item_t *item,
75 input_item_meta_request_option_t options,
76 const input_preparser_callbacks_t *cbs, void *userdata,
77 void *id, vlc_tick_t timeout)
79 assert(timeout >= 0);
81 struct task *task = malloc(sizeof(*task));
82 if (!task)
83 return NULL;
85 task->preparser = preparser;
86 task->item = item;
87 task->options = options;
88 task->cbs = cbs;
89 task->userdata = userdata;
90 task->id = id;
91 task->timeout = timeout;
93 input_item_Hold(item);
95 task->parser = NULL;
96 vlc_mutex_init(&task->lock);
97 vlc_cond_init(&task->cond_ended);
98 task->preparse_ended = false;
99 task->preparse_status = ITEM_PREPARSE_SKIPPED;
100 task->fetch_ended = false;
102 atomic_init(&task->interrupted, false);
104 task->runnable.run = RunnableRun;
105 task->runnable.userdata = task;
107 return task;
110 static void
111 TaskDelete(struct task *task)
113 input_item_Release(task->item);
114 free(task);
117 static void
118 PreparserAddTask(input_preparser_t *preparser, struct task *task)
120 vlc_mutex_lock(&preparser->lock);
121 vlc_list_append(&task->node, &preparser->submitted_tasks);
122 vlc_mutex_unlock(&preparser->lock);
125 static void
126 PreparserRemoveTask(input_preparser_t *preparser, struct task *task)
128 vlc_mutex_lock(&preparser->lock);
129 vlc_list_remove(&task->node);
130 vlc_mutex_unlock(&preparser->lock);
133 static void
134 NotifyPreparseEnded(struct task *task)
136 if (task->cbs && task->cbs->on_preparse_ended)
137 task->cbs->on_preparse_ended(task->item, task->preparse_status,
138 task->userdata);
141 static void
142 OnParserEnded(input_item_t *item, int status, void *task_)
144 VLC_UNUSED(item);
145 struct task *task = task_;
147 if (atomic_load(&task->interrupted))
149 * On interruption, the call to input_item_parser_id_Release() may
150 * trigger this "parser ended" callback. Ignore it.
152 return;
154 vlc_mutex_lock(&task->lock);
155 assert(!task->preparse_ended);
156 task->preparse_status = status == VLC_SUCCESS ? ITEM_PREPARSE_DONE
157 : ITEM_PREPARSE_FAILED;
158 task->preparse_ended = true;
159 vlc_mutex_unlock(&task->lock);
161 vlc_cond_signal(&task->cond_ended);
164 static void
165 OnParserSubtreeAdded(input_item_t *item, input_item_node_t *subtree,
166 void *task_)
168 VLC_UNUSED(item);
169 struct task *task = task_;
171 if (task->cbs && task->cbs->on_subtree_added)
172 task->cbs->on_subtree_added(task->item, subtree, task->userdata);
175 static void
176 OnArtFetchEnded(input_item_t *item, bool fetched, void *userdata)
178 VLC_UNUSED(item);
179 VLC_UNUSED(fetched);
181 struct task *task = userdata;
183 vlc_mutex_lock(&task->lock);
184 assert(!task->fetch_ended);
185 task->fetch_ended = true;
186 vlc_mutex_unlock(&task->lock);
188 vlc_cond_signal(&task->cond_ended);
191 static const input_fetcher_callbacks_t input_fetcher_callbacks = {
192 .on_art_fetch_ended = OnArtFetchEnded,
195 static void
196 Parse(struct task *task, vlc_tick_t deadline)
198 static const input_item_parser_cbs_t cbs = {
199 .on_ended = OnParserEnded,
200 .on_subtree_added = OnParserSubtreeAdded,
203 vlc_object_t *obj = task->preparser->owner;
204 task->parser = input_item_Parse(task->item, obj, &cbs, task);
205 if (!task->parser)
207 task->preparse_status = ITEM_PREPARSE_FAILED;
208 return;
211 /* Wait until the end of parsing */
212 vlc_mutex_lock(&task->lock);
213 if (deadline == VLC_TICK_INVALID)
215 while (!task->preparse_ended)
216 vlc_cond_wait(&task->cond_ended, &task->lock);
218 else
220 bool timeout = false;
221 while (!task->preparse_ended && !timeout)
222 timeout =
223 vlc_cond_timedwait(&task->cond_ended, &task->lock, deadline);
225 if (timeout)
227 task->preparse_status = ITEM_PREPARSE_TIMEOUT;
228 atomic_store(&task->interrupted, true);
231 vlc_mutex_unlock(&task->lock);
233 /* This call also interrupts the parsing if it is still running */
234 input_item_parser_id_Release(task->parser);
237 static void
238 Fetch(struct task *task)
240 input_fetcher_t *fetcher = task->preparser->fetcher;
241 if (!fetcher || !(task->options & META_REQUEST_OPTION_FETCH_ANY))
242 return;
244 int ret =
245 input_fetcher_Push(fetcher, task->item,
246 task->options & META_REQUEST_OPTION_FETCH_ANY,
247 &input_fetcher_callbacks, task);
248 if (ret != VLC_SUCCESS)
249 return;
251 /* Wait until the end of fetching (fetching is not interruptible) */
252 vlc_mutex_lock(&task->lock);
253 while (!task->fetch_ended)
254 vlc_cond_wait(&task->cond_ended, &task->lock);
255 vlc_mutex_unlock(&task->lock);
258 static void
259 RunnableRun(void *userdata)
261 struct task *task = userdata;
263 vlc_tick_t deadline = task->timeout ? vlc_tick_now() + task->timeout
264 : VLC_TICK_INVALID;
266 if (atomic_load(&task->interrupted))
267 goto end;
269 Parse(task, deadline);
271 if (atomic_load(&task->interrupted))
272 goto end;
274 Fetch(task);
276 if (atomic_load(&task->interrupted))
277 goto end;
279 input_item_SetPreparsed(task->item, true);
281 end:
282 NotifyPreparseEnded(task);
283 input_preparser_t *preparser = task->preparser;
284 PreparserRemoveTask(preparser, task);
285 TaskDelete(task);
288 static void
289 Interrupt(struct task *task)
291 assert(!atomic_load(&task->interrupted));
292 atomic_store(&task->interrupted, true);
294 /* Wake up the preparser cond_wait */
295 vlc_mutex_lock(&task->lock);
296 task->preparse_status = ITEM_PREPARSE_TIMEOUT;
297 task->preparse_ended = true;
298 vlc_mutex_unlock(&task->lock);
299 vlc_cond_signal(&task->cond_ended);
302 input_preparser_t* input_preparser_New( vlc_object_t *parent )
304 input_preparser_t* preparser = malloc( sizeof *preparser );
305 if (!preparser)
306 return NULL;
308 int max_threads = var_InheritInteger(parent, "preparse-threads");
309 if (max_threads < 1)
310 max_threads = 1;
312 preparser->executor = vlc_executor_New(max_threads);
313 if (!preparser->executor)
315 free(preparser);
316 return NULL;
319 preparser->default_timeout =
320 VLC_TICK_FROM_MS(var_InheritInteger(parent, "preparse-timeout"));
321 if (preparser->default_timeout < 0)
322 preparser->default_timeout = 0;
324 preparser->owner = parent;
325 preparser->fetcher = input_fetcher_New( parent );
326 atomic_init( &preparser->deactivated, false );
328 vlc_mutex_init(&preparser->lock);
329 vlc_list_init(&preparser->submitted_tasks);
331 if( unlikely( !preparser->fetcher ) )
332 msg_Warn( parent, "unable to create art fetcher" );
334 return preparser;
337 void input_preparser_Push( input_preparser_t *preparser,
338 input_item_t *item, input_item_meta_request_option_t i_options,
339 const input_preparser_callbacks_t *cbs, void *cbs_userdata,
340 int timeout_ms, void *id )
342 if( atomic_load( &preparser->deactivated ) )
343 return;
345 vlc_mutex_lock( &item->lock );
346 enum input_item_type_e i_type = item->i_type;
347 int b_net = item->b_net;
348 if( i_options & META_REQUEST_OPTION_DO_INTERACT )
349 item->b_preparse_interact = true;
350 vlc_mutex_unlock( &item->lock );
352 switch( i_type )
354 case ITEM_TYPE_NODE:
355 case ITEM_TYPE_FILE:
356 case ITEM_TYPE_DIRECTORY:
357 case ITEM_TYPE_PLAYLIST:
358 if( !b_net || i_options & META_REQUEST_OPTION_SCOPE_NETWORK )
359 break;
360 /* fallthrough */
361 default:
362 if (cbs && cbs->on_preparse_ended)
363 cbs->on_preparse_ended(item, ITEM_PREPARSE_SKIPPED, cbs_userdata);
364 return;
367 vlc_tick_t timeout = timeout_ms == -1 ? preparser->default_timeout
368 : VLC_TICK_FROM_MS(timeout_ms);
369 struct task *task =
370 TaskNew(preparser, item, i_options, cbs, cbs_userdata, id, timeout);
372 PreparserAddTask(preparser, task);
374 vlc_executor_Submit(preparser->executor, &task->runnable);
377 void input_preparser_fetcher_Push( input_preparser_t *preparser,
378 input_item_t *item, input_item_meta_request_option_t options,
379 const input_fetcher_callbacks_t *cbs, void *cbs_userdata )
381 if( preparser->fetcher )
382 input_fetcher_Push( preparser->fetcher, item, options,
383 cbs, cbs_userdata );
386 void input_preparser_Cancel( input_preparser_t *preparser, void *id )
388 vlc_mutex_lock(&preparser->lock);
390 struct task *task;
391 vlc_list_foreach(task, &preparser->submitted_tasks, node)
393 if (!id || task->id == id)
395 bool canceled =
396 vlc_executor_Cancel(preparser->executor, &task->runnable);
397 if (canceled)
399 NotifyPreparseEnded(task);
400 vlc_list_remove(&task->node);
401 TaskDelete(task);
403 else
404 /* The task will be finished and destroyed after run() */
405 Interrupt(task);
409 vlc_mutex_unlock(&preparser->lock);
412 void input_preparser_Deactivate( input_preparser_t* preparser )
414 atomic_store( &preparser->deactivated, true );
415 input_preparser_Cancel(preparser, NULL);
418 void input_preparser_Delete( input_preparser_t *preparser )
420 /* In case input_preparser_Deactivate() has not been called */
421 input_preparser_Cancel(preparser, NULL);
423 vlc_executor_Delete(preparser->executor);
425 if( preparser->fetcher )
426 input_fetcher_Delete( preparser->fetcher );
428 free( preparser );