1 /*****************************************************************************
2 * Copyright (C) 2017 VLC authors and VideoLAN
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; either version 2.1 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
17 *****************************************************************************/
24 #include <vlc_common.h>
25 #include <vlc_threads.h>
26 #include <vlc_arrays.h>
29 #include "background_worker.h"
31 struct bg_queued_item
{
32 void* id
; /**< id associated with entity */
33 void* entity
; /**< the entity to process */
34 int timeout
; /**< timeout duration in microseconds */
37 struct background_worker
{
39 struct background_worker_config conf
;
41 vlc_mutex_t lock
; /**< acquire to inspect members that follow */
43 bool probe_request
; /**< true if a probe is requested */
44 vlc_cond_t wait
; /**< wait for update in terms of head */
45 vlc_cond_t worker_wait
; /**< wait for probe request or cancelation */
46 mtime_t deadline
; /**< deadline of the current task */
47 void* id
; /**< id of the current task */
48 bool active
; /**< true if there is an active thread */
52 vlc_cond_t wait
; /**< wait for update in terms of tail */
53 vlc_array_t data
; /**< queue of pending entities to process */
57 static void* Thread( void* data
)
59 struct background_worker
* worker
= data
;
63 struct bg_queued_item
* item
= NULL
;
66 vlc_mutex_lock( &worker
->lock
);
69 if( vlc_array_count( &worker
->tail
.data
) )
71 item
= vlc_array_item_at_index( &worker
->tail
.data
, 0 );
74 vlc_array_remove( &worker
->tail
.data
, 0 );
77 if( worker
->head
.deadline
== VLC_TS_0
&& item
== NULL
)
78 worker
->head
.active
= false;
79 worker
->head
.id
= item
? item
->id
: NULL
;
80 vlc_cond_broadcast( &worker
->head
.wait
);
84 if( item
->timeout
> 0 )
85 worker
->head
.deadline
= mdate() + item
->timeout
* 1000;
87 worker
->head
.deadline
= INT64_MAX
;
89 else if( worker
->head
.deadline
!= VLC_TS_0
)
91 /* Wait 1 seconds for new inputs before terminating */
92 mtime_t deadline
= mdate() + INT64_C(1000000);
93 int ret
= vlc_cond_timedwait( &worker
->tail
.wait
,
94 &worker
->lock
, deadline
);
97 /* Timeout: if there is still no items, the thread will be
98 * terminated at next loop iteration (active = false). */
99 worker
->head
.deadline
= VLC_TS_0
;
106 if( !worker
->head
.active
)
108 vlc_mutex_unlock( &worker
->lock
);
111 vlc_mutex_unlock( &worker
->lock
);
113 assert( item
!= NULL
);
115 if( worker
->conf
.pf_start( worker
->owner
, item
->entity
, &handle
) )
117 worker
->conf
.pf_release( item
->entity
);
124 vlc_mutex_lock( &worker
->lock
);
126 bool const b_timeout
= worker
->head
.deadline
<= mdate();
127 worker
->head
.probe_request
= false;
129 vlc_mutex_unlock( &worker
->lock
);
132 worker
->conf
.pf_probe( worker
->owner
, handle
) )
134 worker
->conf
.pf_stop( worker
->owner
, handle
);
135 worker
->conf
.pf_release( item
->entity
);
140 vlc_mutex_lock( &worker
->lock
);
141 if( worker
->head
.probe_request
== false &&
142 worker
->head
.deadline
> mdate() )
144 vlc_cond_timedwait( &worker
->head
.worker_wait
, &worker
->lock
,
145 worker
->head
.deadline
);
147 vlc_mutex_unlock( &worker
->lock
);
154 static void BackgroundWorkerCancel( struct background_worker
* worker
, void* id
)
156 vlc_mutex_lock( &worker
->lock
);
157 for( size_t i
= 0; i
< vlc_array_count( &worker
->tail
.data
); )
159 struct bg_queued_item
* item
=
160 vlc_array_item_at_index( &worker
->tail
.data
, i
);
162 if( id
== NULL
|| item
->id
== id
)
164 vlc_array_remove( &worker
->tail
.data
, i
);
165 worker
->conf
.pf_release( item
->entity
);
173 while( ( id
== NULL
&& worker
->head
.active
)
174 || ( id
!= NULL
&& worker
->head
.id
== id
) )
176 worker
->head
.deadline
= VLC_TS_0
;
177 vlc_cond_signal( &worker
->head
.worker_wait
);
178 vlc_cond_signal( &worker
->tail
.wait
);
179 vlc_cond_wait( &worker
->head
.wait
, &worker
->lock
);
181 vlc_mutex_unlock( &worker
->lock
);
184 struct background_worker
* background_worker_New( void* owner
,
185 struct background_worker_config
* conf
)
187 struct background_worker
* worker
= malloc( sizeof *worker
);
189 if( unlikely( !worker
) )
192 worker
->conf
= *conf
;
193 worker
->owner
= owner
;
194 worker
->head
.id
= NULL
;
195 worker
->head
.active
= false;
196 worker
->head
.deadline
= VLC_TS_INVALID
;
198 vlc_mutex_init( &worker
->lock
);
199 vlc_cond_init( &worker
->head
.wait
);
200 vlc_cond_init( &worker
->head
.worker_wait
);
202 vlc_array_init( &worker
->tail
.data
);
203 vlc_cond_init( &worker
->tail
.wait
);
208 int background_worker_Push( struct background_worker
* worker
, void* entity
,
209 void* id
, int timeout
)
211 struct bg_queued_item
* item
= malloc( sizeof( *item
) );
213 if( unlikely( !item
) )
217 item
->entity
= entity
;
218 item
->timeout
= timeout
< 0 ? worker
->conf
.default_timeout
: timeout
;
220 vlc_mutex_lock( &worker
->lock
);
221 vlc_array_append( &worker
->tail
.data
, item
);
222 vlc_cond_signal( &worker
->tail
.wait
);
224 if( worker
->head
.active
== false )
226 worker
->head
.probe_request
= false;
227 worker
->head
.active
=
228 !vlc_clone_detach( NULL
, Thread
, worker
, VLC_THREAD_PRIORITY_LOW
);
231 if( worker
->head
.active
)
232 worker
->conf
.pf_hold( item
->entity
);
234 int ret
= worker
->head
.active
? VLC_SUCCESS
: VLC_EGENERIC
;
235 vlc_mutex_unlock( &worker
->lock
);
240 void background_worker_Cancel( struct background_worker
* worker
, void* id
)
242 BackgroundWorkerCancel( worker
, id
);
245 void background_worker_RequestProbe( struct background_worker
* worker
)
247 vlc_mutex_lock( &worker
->lock
);
248 worker
->head
.probe_request
= true;
249 vlc_cond_signal( &worker
->head
.worker_wait
);
250 vlc_mutex_unlock( &worker
->lock
);
253 void background_worker_Delete( struct background_worker
* worker
)
255 BackgroundWorkerCancel( worker
, NULL
);
256 vlc_array_clear( &worker
->tail
.data
);
257 vlc_mutex_destroy( &worker
->lock
);
258 vlc_cond_destroy( &worker
->head
.wait
);
259 vlc_cond_destroy( &worker
->head
.worker_wait
);
260 vlc_cond_destroy( &worker
->tail
.wait
);