/*****************************************************************************
* src/test/executor.c
*****************************************************************************
* Copyright (C) 2020 Videolabs, VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#undef NDEBUG
#include <assert.h>
#include <vlc_common.h>
#include <vlc_threads.h>
#include <vlc_executor.h>
#include <vlc_tick.h>
struct data
{
vlc_mutex_t lock;
vlc_cond_t cond;
int started;
int ended;
vlc_tick_t delay;
};
static void InitData(struct data *data)
{
vlc_mutex_init(&data->lock);
vlc_cond_init(&data->cond);
data->started = 0;
data->ended = 0;
data->delay = 0;
}
static void RunIncrement(void *userdata)
{
struct data *data = userdata;
vlc_mutex_lock(&data->lock);
++data->started;
vlc_mutex_unlock(&data->lock);
if (data->delay > 0)
vlc_tick_sleep(data->delay);
vlc_mutex_lock(&data->lock);
++data->ended;
vlc_cond_signal(&data->cond);
vlc_mutex_unlock(&data->lock);
}
static void test_single_runnable(void)
{
vlc_executor_t *executor = vlc_executor_New(1);
assert(executor);
struct data data;
InitData(&data);
struct vlc_runnable runnable = {
.run = RunIncrement,
.userdata = &data,
};
vlc_executor_Submit(executor, &runnable);
vlc_mutex_lock(&data.lock);
while (data.ended == 0)
vlc_cond_wait(&data.cond, &data.lock);
vlc_mutex_unlock(&data.lock);
assert(data.ended == 1);
vlc_executor_Delete(executor);
}
static void test_multiple_runnables(void)
{
vlc_executor_t *executor = vlc_executor_New(3);
assert(executor);
struct data shared_data;
InitData(&shared_data);
struct vlc_runnable runnables[300];
for (int i = 0; i < 300; ++i)
{
struct vlc_runnable *runnable = &runnables[i];
runnable->run = RunIncrement;
runnable->userdata = &shared_data;
vlc_executor_Submit(executor, runnable);
}
vlc_mutex_lock(&shared_data.lock);
while (shared_data.ended < 300)
vlc_cond_wait(&shared_data.cond, &shared_data.lock);
vlc_mutex_unlock(&shared_data.lock);
assert(shared_data.ended == 300);
vlc_executor_Delete(executor);
}
static void test_blocking_delete(void)
{
vlc_executor_t *executor = vlc_executor_New(1);
assert(executor);
struct data data;
InitData(&data);
data.delay = VLC_TICK_FROM_MS(100);
struct vlc_runnable runnable = {
.run = RunIncrement,
.userdata = &data,
};
vlc_executor_Submit(executor, &runnable);
/* Wait for the runnable to be started */
vlc_mutex_lock(&data.lock);
while (data.started == 0)
vlc_cond_wait(&data.cond, &data.lock);
vlc_mutex_unlock(&data.lock);
/* The runnable sleeps for about 100ms */
vlc_executor_Delete(executor);
vlc_mutex_lock(&data.lock);
/* The executor must wait the end of running tasks on delete */
assert(data.ended == 1);
vlc_mutex_unlock(&data.lock);
}
static void test_cancel(void)
{
vlc_executor_t *executor = vlc_executor_New(4);
assert(executor);
struct data shared_data;
InitData(&shared_data);
shared_data.delay = VLC_TICK_FROM_MS(100);
/* Submit 40 tasks taking at least 100ms on an executor with at most 4
* threads */
struct vlc_runnable runnables[40];
for (int i = 0; i < 40; ++i)
{
struct vlc_runnable *runnable = &runnables[i];
runnable->run = RunIncrement;
runnable->userdata = &shared_data;
vlc_executor_Submit(executor, runnable);
}
/* Wait a bit (in two lines to avoid harmful_delay() warning) */
vlc_tick_t delay = VLC_TICK_FROM_MS(150);
vlc_tick_sleep(delay);
int canceled = 0;
for (int i = 0; i < 40; ++i)
{
if (vlc_executor_Cancel(executor, &runnables[i]))
++canceled;
}
vlc_mutex_lock(&shared_data.lock);
/* All started must not be canceled (but some non-started-yet may not be
* canceled either) */
assert(canceled + shared_data.started <= 40);
vlc_mutex_unlock(&shared_data.lock);
vlc_executor_Delete(executor);
/* Every started task must also be ended */
assert(shared_data.started == shared_data.ended);
/* Every task is either canceled or ended */
assert(canceled + shared_data.ended == 40);
}
struct doubler_task
{
vlc_executor_t *executor;
int *array;
size_t count;
struct vlc_runnable runnable;
};
static void DoublerRun(void *);
static bool
SpawnDoublerTask(vlc_executor_t *executor, int *array, size_t count)
{
struct doubler_task *task = malloc(sizeof(*task));
if (!task)
return false;
task->executor = executor;
task->array = array;
task->count = count;
task->runnable.run = DoublerRun;
task->runnable.userdata = task;
vlc_executor_Submit(executor, &task->runnable);
return true;
}
static void DoublerRun(void *userdata)
{
struct doubler_task *task = userdata;
if (task->count == 1)
task->array[0] *= 2; /* double the value */
else
{
/* Spawn tasks doubling halves of the array recursively */
bool ok;
ok = SpawnDoublerTask(task->executor, task->array, task->count / 2);
assert(ok);
ok = SpawnDoublerTask(task->executor, task->array + task->count / 2,
task->count - task->count / 2);
assert(ok);
}
free(task);
}
static void test_task_chain(void)
{
vlc_executor_t *executor = vlc_executor_New(4);
assert(executor);
/* Numbers from 0 to 99 */
int array[100];
for (int i = 0; i < 100; ++i)
array[i] = i;
/* Double all values in the array from tasks spawning smaller tasks
* recursively, until the array has size 1, where the single value is
* doubled */
SpawnDoublerTask(executor, array, 100);
vlc_executor_WaitIdle(executor);
vlc_executor_Delete(executor);
/* All values must have been doubled */
for (int i = 0; i < 100; ++i)
assert(array[i] == 2 * i);
}
int main(void)
{
test_single_runnable();
test_multiple_runnables();
test_blocking_delete();
test_cancel();
test_task_chain();
return 0;
}
¤ Dauer der Verarbeitung: 0.4 Sekunden
¤
*© Formatika GbR, Deutschland