[vlc-devel] [PATCH 2/6] executor: add unit tests

Alexandre Janniaux ajanni at videolabs.io
Mon Sep 7 18:27:37 CEST 2020


Hi,

On Mon, Sep 07, 2020 at 05:40:07PM +0200, Romain Vimont wrote:
> ---
>  test/Makefile.am         |   3 +
>  test/src/misc/executor.c | 282 +++++++++++++++++++++++++++++++++++++++
>  2 files changed, 285 insertions(+)
>  create mode 100644 test/src/misc/executor.c
>
> diff --git a/test/Makefile.am b/test/Makefile.am
> index c2ce32d02f..79989ad30e 100644
> --- a/test/Makefile.am
> +++ b/test/Makefile.am
> @@ -33,6 +33,7 @@ check_PROGRAMS = \
>  	test_src_misc_bits \
>  	test_src_misc_epg \
>  	test_src_misc_keystore \
> +	test_src_misc_executor \
>  	test_modules_packetizer_helpers \
>  	test_modules_packetizer_hxxx \
>  	test_modules_packetizer_h264 \
> @@ -141,6 +142,8 @@ test_src_interface_dialog_SOURCES = src/interface/dialog.c
>  test_src_interface_dialog_LDADD = $(LIBVLCCORE) $(LIBVLC)
>  test_src_media_source_LDADD = $(LIBVLCCORE) $(LIBVLC)
>  test_src_media_source_SOURCES = src/media_source/media_source.c
> +test_src_misc_executor_LDADD = $(LIBVLCCORE) $(LIBVLC)
> +test_src_misc_executor_SOURCES = src/misc/executor.c
>  test_modules_packetizer_helpers_SOURCES = modules/packetizer/helpers.c
>  test_modules_packetizer_helpers_LDADD = $(LIBVLCCORE) $(LIBVLC)
>  test_modules_packetizer_hxxx_SOURCES = modules/packetizer/hxxx.c
> diff --git a/test/src/misc/executor.c b/test/src/misc/executor.c
> new file mode 100644
> index 0000000000..678693b82a
> --- /dev/null
> +++ b/test/src/misc/executor.c
> @@ -0,0 +1,282 @@
> +/*****************************************************************************
> + * test/src/misc/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
> +
> +#include "../../libvlc/test.h"
> +#include "../lib/libvlc_internal.h"
> +
> +#include <assert.h>
> +#include <vlc_common.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_mutex_unlock(&data->lock);
> +
> +    vlc_cond_signal(&data->cond);
> +}
> +
> +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) */

To avoid harmful_delay() warnings, you can use (vlc_tick_sleep)(foo);

> +    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;
> +}
> --
> 2.28.0
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel


More information about the vlc-devel mailing list