1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2020 Videolabs, 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 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_executor.h>
42 static void InitData(struct data
*data
)
44 vlc_mutex_init(&data
->lock
);
45 vlc_cond_init(&data
->cond
);
51 static void RunIncrement(void *userdata
)
53 struct data
*data
= userdata
;
55 vlc_mutex_lock(&data
->lock
);
57 vlc_mutex_unlock(&data
->lock
);
60 vlc_tick_sleep(data
->delay
);
62 vlc_mutex_lock(&data
->lock
);
64 vlc_mutex_unlock(&data
->lock
);
66 vlc_cond_signal(&data
->cond
);
69 static void test_single_runnable(void)
71 vlc_executor_t
*executor
= vlc_executor_New(1);
77 struct vlc_runnable runnable
= {
82 vlc_executor_Submit(executor
, &runnable
);
84 vlc_mutex_lock(&data
.lock
);
85 while (data
.ended
== 0)
86 vlc_cond_wait(&data
.cond
, &data
.lock
);
87 vlc_mutex_unlock(&data
.lock
);
89 assert(data
.ended
== 1);
91 vlc_executor_Delete(executor
);
94 static void test_multiple_runnables(void)
96 vlc_executor_t
*executor
= vlc_executor_New(3);
99 struct data shared_data
;
100 InitData(&shared_data
);
102 struct vlc_runnable runnables
[300];
103 for (int i
= 0; i
< 300; ++i
)
105 struct vlc_runnable
*runnable
= &runnables
[i
];
106 runnable
->run
= RunIncrement
;
107 runnable
->userdata
= &shared_data
;
108 vlc_executor_Submit(executor
, runnable
);
111 vlc_mutex_lock(&shared_data
.lock
);
112 while (shared_data
.ended
< 300)
113 vlc_cond_wait(&shared_data
.cond
, &shared_data
.lock
);
114 vlc_mutex_unlock(&shared_data
.lock
);
116 assert(shared_data
.ended
== 300);
118 vlc_executor_Delete(executor
);
121 static void test_blocking_delete(void)
123 vlc_executor_t
*executor
= vlc_executor_New(1);
129 data
.delay
= VLC_TICK_FROM_MS(100);
131 struct vlc_runnable runnable
= {
136 vlc_executor_Submit(executor
, &runnable
);
138 /* Wait for the runnable to be started */
139 vlc_mutex_lock(&data
.lock
);
140 while (data
.started
== 0)
141 vlc_cond_wait(&data
.cond
, &data
.lock
);
142 vlc_mutex_unlock(&data
.lock
);
144 /* The runnable sleeps for about 100ms */
146 vlc_executor_Delete(executor
);
148 vlc_mutex_lock(&data
.lock
);
149 /* The executor must wait the end of running tasks on delete */
150 assert(data
.ended
== 1);
151 vlc_mutex_unlock(&data
.lock
);
154 static void test_cancel(void)
156 vlc_executor_t
*executor
= vlc_executor_New(4);
159 struct data shared_data
;
160 InitData(&shared_data
);
162 shared_data
.delay
= VLC_TICK_FROM_MS(100);
164 /* Submit 40 tasks taking at least 100ms on an executor with at most 4
167 struct vlc_runnable runnables
[40];
168 for (int i
= 0; i
< 40; ++i
)
170 struct vlc_runnable
*runnable
= &runnables
[i
];
171 runnable
->run
= RunIncrement
;
172 runnable
->userdata
= &shared_data
;
173 vlc_executor_Submit(executor
, runnable
);
176 /* Wait a bit (in two lines to avoid harmful_delay() warning) */
177 vlc_tick_t delay
= VLC_TICK_FROM_MS(150);
178 vlc_tick_sleep(delay
);
181 for (int i
= 0; i
< 40; ++i
)
183 if (vlc_executor_Cancel(executor
, &runnables
[i
]))
187 vlc_mutex_lock(&shared_data
.lock
);
188 /* All started must not be canceled (but some non-started-yet may not be
189 * canceled either) */
190 assert(canceled
+ shared_data
.started
<= 40);
191 vlc_mutex_unlock(&shared_data
.lock
);
193 vlc_executor_Delete(executor
);
195 /* Every started task must also be ended */
196 assert(shared_data
.started
== shared_data
.ended
);
197 /* Every task is either canceled or ended */
198 assert(canceled
+ shared_data
.ended
== 40);
203 vlc_executor_t
*executor
;
206 struct vlc_runnable runnable
;
209 static void DoublerRun(void *);
212 SpawnDoublerTask(vlc_executor_t
*executor
, int *array
, size_t count
)
214 struct doubler_task
*task
= malloc(sizeof(*task
));
218 task
->executor
= executor
;
221 task
->runnable
.run
= DoublerRun
;
222 task
->runnable
.userdata
= task
;
224 vlc_executor_Submit(executor
, &task
->runnable
);
229 static void DoublerRun(void *userdata
)
231 struct doubler_task
*task
= userdata
;
233 if (task
->count
== 1)
234 task
->array
[0] *= 2; /* double the value */
237 /* Spawn tasks doubling halves of the array recursively */
240 ok
= SpawnDoublerTask(task
->executor
, task
->array
, task
->count
/ 2);
243 ok
= SpawnDoublerTask(task
->executor
, task
->array
+ task
->count
/ 2,
244 task
->count
- task
->count
/ 2);
251 static void test_task_chain(void)
253 vlc_executor_t
*executor
= vlc_executor_New(4);
256 /* Numbers from 0 to 99 */
258 for (int i
= 0; i
< 100; ++i
)
261 /* Double all values in the array from tasks spawning smaller tasks
262 * recursively, until the array has size 1, where the single value is
264 SpawnDoublerTask(executor
, array
, 100);
266 vlc_executor_WaitIdle(executor
);
267 vlc_executor_Delete(executor
);
269 /* All values must have been doubled */
270 for (int i
= 0; i
< 100; ++i
)
271 assert(array
[i
] == 2 * i
);
276 test_single_runnable();
277 test_multiple_runnables();
278 test_blocking_delete();