[vlc-devel] [PATCH v5] test: add thread test
Marvin Scholz
epirat07 at gmail.com
Thu Mar 26 14:26:32 CET 2020
On 26 Mar 2020, at 14:22, Steve Lhomme wrote:
> This version still has the vlc_tick_sleep(0) so fails to compile for
> me.
>
Oh right, sorry, will send a new version.
> On 2020-03-24 20:36, Marvin Scholz wrote:
>> ---
>> src/Makefile.am | 4 +-
>> src/test/thread.c | 369
>> ++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 372 insertions(+), 1 deletion(-)
>> create mode 100644 src/test/thread.c
>>
>> diff --git a/src/Makefile.am b/src/Makefile.am
>> index e342b78a51..88c6d903ae 100644
>> --- a/src/Makefile.am
>> +++ b/src/Makefile.am
>> @@ -586,7 +586,8 @@ check_PROGRAMS = \
>> test_playlist \
>> test_randomizer \
>> test_media_source \
>> - test_extensions
>> + test_extensions \
>> + test_thread
>> TESTS = $(check_PROGRAMS) check_symbols
>> @@ -632,6 +633,7 @@ test_media_source_CFLAGS = -DTEST_MEDIA_SOURCE
>> test_media_source_SOURCES = media_source/test.c \
>> media_source/media_source.c \
>> media_source/media_tree.c
>> +test_thread_SOURCES = test/thread.c
>> AM_LDFLAGS = -no-install
>> LDADD = libvlccore.la \
>> diff --git a/src/test/thread.c b/src/test/thread.c
>> new file mode 100644
>> index 0000000000..4c5d62a61d
>> --- /dev/null
>> +++ b/src/test/thread.c
>> @@ -0,0 +1,369 @@
>> +/*****************************************************************************
>> + * thread.c: Test for thread API
>> +
>> *****************************************************************************
>> + * Copyright (C) 2020 Marvin Scholz
>> + *
>> + * 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 <stdio.h>
>> +#include <stdlib.h>
>> +#include <errno.h>
>> +#undef NDEBUG
>> +#include <assert.h>
>> +
>> +#include <vlc_common.h>
>> +
>> +static int thread_data_magic = 0x1234;
>> +static int thread_return_magic = 0x4321;
>> +
>> +// Join thread and verify return
>> +#define TEST_THREAD_JOIN(th, eret) \
>> + do { \
>> + void *r; \
>> + vlc_join(th, &r); \
>> + assert(r == eret); \
>> + } while (0)
>> +
>> +// Cancels thread, joins and verifies return
>> +#define TEST_THREAD_CANCEL_JOIN(th) \
>> + do { \
>> + vlc_cancel(th); \
>> + TEST_THREAD_JOIN(th, VLC_THREAD_CANCELED); \
>> + } while (0)
>> +
>> +// Join thread and verify magic return value
>> +#define TEST_THREAD_JOIN_MAGIC(th) \
>> + TEST_THREAD_JOIN(th, &thread_return_magic)
>> +
>> +// Spawn thread
>> +#define TEST_THREAD_CLONE(th, f, data) \
>> + assert(vlc_clone(th, f, data, VLC_THREAD_PRIORITY_LOW) == 0)
>> +
>> +// Spawn thread with magic data
>> +#define TEST_THREAD_CLONE_MAGIC(th, f) \
>> + TEST_THREAD_CLONE(th, f, &thread_data_magic)
>> +
>> +
>> +// Structure for the condition tests
>> +struct cond_data
>> +{
>> + int state; // Count of threads starting, or -1 when threads may
>> exit
>> + vlc_mutex_t mutex;
>> + vlc_cond_t cond_started; // Signalled when a thread started
>> + vlc_cond_t cond_end; // Waited on by thread before
>> terminating
>> +};
>> +
>> +static void cond_data_init(struct cond_data *data, int count)
>> +{
>> + assert(count > 0);
>> +
>> + data->state = count;
>> + vlc_mutex_init(&data->mutex);
>> + vlc_cond_init(&data->cond_started);
>> + vlc_cond_init(&data->cond_end);
>> +}
>> +
>> +
>> +/*
>> + * Test simple thread creation
>> + */
>> +static void *thread_func_noop(void *data)
>> +{
>> + assert(data == &thread_data_magic);
>> + return &thread_return_magic;
>> +}
>> +
>> +static void test__thread_create()
>> +{
>> + vlc_thread_t th;
>> + TEST_THREAD_CLONE_MAGIC(&th, thread_func_noop);
>> + TEST_THREAD_JOIN_MAGIC(th);
>> +}
>> +
>> +
>> +/*
>> + * Test simple thread cancelation
>> + */
>> +static void *thread_func_loop(void *data)
>> +{
>> + assert(data == &thread_data_magic);
>> + while(1) {
>> + sleep(10);
>> + }
>> + return &thread_return_magic;
>> +}
>> +
>> +static void test__thread_cancelation()
>> +{
>> + vlc_thread_t th;
>> + TEST_THREAD_CLONE_MAGIC(&th, thread_func_loop);
>> + TEST_THREAD_CANCEL_JOIN(th);
>> +}
>> +
>> +
>> +/*
>> + * Test thread cancellation with cleanup
>> + */
>> +static void thread_func_cleanup(void *data)
>> +{
>> + int *cleanup_state = data;
>> + assert(*cleanup_state == 0);
>> + *cleanup_state = 1;
>> +}
>> +
>> +static void *thread_func_loop_cleanup(void *data)
>> +{
>> + int *cleanup_state = data;
>> + assert(*cleanup_state == 0);
>> +
>> + vlc_cleanup_push(thread_func_cleanup, data);
>> + while(1) {
>> + vlc_testcancel();
>> + }
>> + vlc_cleanup_pop();
>> +
>> + return &thread_return_magic;
>> +}
>> +
>> +static void test__thread_cancelation_with_cleanup()
>> +{
>> + vlc_thread_t th;
>> + int th_cleanup_state = 0;
>> +
>> + TEST_THREAD_CLONE(&th, thread_func_loop_cleanup,
>> &th_cleanup_state);
>> + TEST_THREAD_CANCEL_JOIN(th);
>> + assert(th_cleanup_state == 1);
>> +}
>> +
>> +
>> +/*
>> + * - Test broadcasting a condition, waking several threads
>> + * - Test waiting on condition
>> + * - Test thread cancelling/cleanup while its waiting on condition
>> + */
>> +static void *thread_func_cond(void *ptr)
>> +{
>> + struct cond_data *data = ptr;
>> +
>> + // Decrement state and signal start of thread
>> + vlc_mutex_lock(&data->mutex);
>> + data->state -= 1;
>> + vlc_cond_signal(&data->cond_started);
>> + vlc_mutex_unlock(&data->mutex);
>> +
>> + // Wait until termination is allowed
>> + vlc_mutex_lock(&data->mutex);
>> + mutex_cleanup_push(&data->mutex);
>> + while(data->state != -1) {
>> + vlc_cond_wait(&data->cond_end, &data->mutex);
>> + }
>> + vlc_cleanup_pop();
>> + vlc_mutex_unlock(&data->mutex);
>> +
>> + return &thread_return_magic;
>> +}
>> +
>> +static void test__cond_broadcast()
>> +{
>> + struct cond_data data;
>> + vlc_thread_t threads[20];
>> +
>> + cond_data_init(&data, ARRAY_SIZE(threads));
>> +
>> + // Spawn all threads
>> + for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) {
>> + TEST_THREAD_CLONE(&threads[i], thread_func_cond, &data);
>> + }
>> +
>> + // Wait for all threads to start
>> + vlc_mutex_lock(&data.mutex);
>> + while(data.state > 0) {
>> + vlc_cond_wait(&data.cond_started, &data.mutex);
>> + }
>> + vlc_mutex_unlock(&data.mutex);
>> +
>> + // Broadcast threads-may-end condition to all threads
>> + vlc_mutex_lock(&data.mutex);
>> + data.state = -1;
>> + vlc_cond_broadcast(&data.cond_end);
>> + vlc_mutex_unlock(&data.mutex);
>> +
>> + // Wait for all threads to exit
>> + for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) {
>> + TEST_THREAD_JOIN_MAGIC(threads[i]);
>> + }
>> +}
>> +
>> +static void test__cond_wait()
>> +{
>> + struct cond_data data;
>> + cond_data_init(&data, 1);
>> +
>> + vlc_thread_t th;
>> + TEST_THREAD_CLONE(&th, thread_func_cond, &data);
>> +
>> + // Wait for thread to start
>> + vlc_mutex_lock(&data.mutex);
>> + while(data.state != 0) {
>> + vlc_cond_wait(&data.cond_started, &data.mutex);
>> + }
>> + vlc_mutex_unlock(&data.mutex);
>> +
>> + // Signal threads-may-end condition to thread
>> + vlc_mutex_lock(&data.mutex);
>> + data.state = -1;
>> + vlc_cond_signal(&data.cond_end);
>> + vlc_mutex_unlock(&data.mutex);
>> +
>> + // Wait for thread to exit
>> + TEST_THREAD_JOIN_MAGIC(th);
>> +}
>> +
>> +static void test__cond_wait_cancelation_with_cleanup()
>> +{
>> + struct cond_data data;
>> + cond_data_init(&data, 1);
>> +
>> + vlc_thread_t th;
>> + TEST_THREAD_CLONE(&th, thread_func_cond, &data);
>> +
>> + // Wait for thread to start
>> + vlc_mutex_lock(&data.mutex);
>> + while(data.state != 0) {
>> + vlc_cond_wait(&data.cond_started, &data.mutex);
>> + }
>> + vlc_mutex_unlock(&data.mutex);
>> +
>> + // Never signal that the thread may end, on purpose
>> + // as thread should be waiting while we cancel it
>> +
>> + // Cancel thread
>> + TEST_THREAD_CANCEL_JOIN(th);
>> +}
>> +
>> +
>> +/*
>> + * Test waiting on condition and let it timeout
>> + */
>> +static void *thread_func_cond_timeout(void *ptr)
>> +{
>> + struct cond_data *data = ptr;
>> +
>> + // Decrement state and signal start of thread
>> + vlc_mutex_lock(&data->mutex);
>> + data->state -= 1;
>> + vlc_cond_signal(&data->cond_started);
>> + vlc_mutex_unlock(&data->mutex);
>> +
>> + vlc_mutex_lock(&data->mutex);
>> + mutex_cleanup_push(&data->mutex);
>> +
>> + // Wait until termination is allowed (which is never signalled)
>> + vlc_tick_t now = vlc_tick_now();
>> + vlc_tick_t deadline = now + VLC_TICK_FROM_MS(25);
>> + int ret = vlc_cond_timedwait(&data->cond_end, &data->mutex,
>> deadline);
>> + assert(ret == ETIMEDOUT);
>> +
>> + vlc_tick_t elapsed = vlc_tick_now() - now;
>> + assert(elapsed >= VLC_TICK_FROM_MS(25));
>> +
>> + vlc_cleanup_pop();
>> + vlc_mutex_unlock(&data->mutex);
>> +
>> + return &thread_return_magic;
>> +}
>> +
>> +static void test__cond_wait_timeout()
>> +{
>> + struct cond_data data;
>> + cond_data_init(&data, 1);
>> +
>> + vlc_thread_t th;
>> + TEST_THREAD_CLONE(&th, thread_func_cond_timeout, &data);
>> +
>> + // Wait for thread to start
>> + vlc_mutex_lock(&data.mutex);
>> + while(data.state != 0) {
>> + vlc_cond_wait(&data.cond_started, &data.mutex);
>> + }
>> + vlc_mutex_unlock(&data.mutex);
>> +
>> + // Never signal that the thread may end, on purpose
>> + // as it should timeout.
>> +
>> + // Wait for thread exit
>> + TEST_THREAD_JOIN_MAGIC(th);
>> +}
>> +
>> +
>> +/*
>> + * Test cancelling vlc_tick_sleep
>> + */
>> +static void *thread_func_tick_sleep(void *data)
>> +{
>> + assert(data == &thread_data_magic);
>> + vlc_tick_sleep(VLC_TICK_FROM_SEC(10));
>> + return &thread_return_magic;
>> +}
>> +
>> +static void test__vlc_tick_sleep_cancelation()
>> +{
>> + vlc_thread_t th;
>> + TEST_THREAD_CLONE_MAGIC(&th, thread_func_tick_sleep);
>> + TEST_THREAD_CANCEL_JOIN(th);
>> +}
>> +
>> +
>> +/*
>> + * - Test sleeping for delay with vlc_tick_sleep
>> + * - Test sleeping for 0 ticks
>> + */
>> +static void test__vlc_tick_sleep()
>> +{
>> + vlc_tick_t now = vlc_tick_now();
>> +
>> + vlc_tick_sleep(VLC_TICK_FROM_MS(25));
>> +
>> + vlc_tick_t elapsed = vlc_tick_now() - now;
>> + assert(elapsed >= VLC_TICK_FROM_MS(25));
>> +
>> + vlc_tick_sleep(0);
>> +}
>> +
>> +
>> +/*
>> + * Main function executing all tests above
>> + */
>> +int main(void)
>> +{
>> + alarm(4);
>> +
>> + test__thread_create();
>> + test__thread_cancelation();
>> + test__thread_cancelation_with_cleanup();
>> + test__cond_wait();
>> + test__cond_wait_cancelation_with_cleanup();
>> + test__cond_wait_timeout();
>> + test__cond_broadcast();
>> + test__vlc_tick_sleep_cancelation();
>> + test__vlc_tick_sleep();
>> +
>> + return 0;
>> +}
>> --
>> 2.21.1 (Apple Git-122.3)
>>
>> _______________________________________________
>> vlc-devel mailing list
>> To unsubscribe or modify your subscription options:
>> https://mailman.videolan.org/listinfo/vlc-devel
>>
> _______________________________________________
> 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