[vlc-devel] [PATCH v2] test: add thread test
Marvin Scholz
epirat07 at gmail.com
Fri Mar 20 19:03:21 CET 2020
---
src/Makefile.am | 4 +-
src/test/thread.c | 340 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 343 insertions(+), 1 deletion(-)
create mode 100644 src/test/thread.c
diff --git a/src/Makefile.am b/src/Makefile.am
index d519a382b6..9890f58870 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..2400fc5026
--- /dev/null
+++ b/src/test/thread.c
@@ -0,0 +1,340 @@
+/*****************************************************************************
+ * 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)
+
+struct cond_data
+{
+ int state;
+ vlc_mutex_t mutex;
+ vlc_cond_t cond_1;
+ vlc_cond_t cond_2;
+};
+
+
+/*
+ * Thread entry point functions
+ *
+ * All of these should return &thread_return_magic which
+ * is used by TEST_THREAD_JOIN_MAGIC.
+ */
+
+static void *thread_func_noop(void *data)
+{
+ assert(data == &thread_data_magic);
+ return &thread_return_magic;
+}
+
+static void *thread_func_loop(void *data)
+{
+ assert(data == &thread_data_magic);
+ while(1) {
+ vlc_testcancel();
+ }
+ return &thread_return_magic;
+}
+
+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 *thread_func_cond_states(void *ptr)
+{
+ struct cond_data *data = ptr;
+
+ // Change to state 1
+ vlc_mutex_lock(&data->mutex);
+ assert(data->state == 0);
+ data->state = 1;
+ vlc_cond_signal(&data->cond_1);
+ vlc_mutex_unlock(&data->mutex);
+
+ // Wait for state 2
+ vlc_mutex_lock(&data->mutex);
+ mutex_cleanup_push(&data->mutex);
+ while(data->state != 2) {
+ vlc_cond_wait(&data->cond_2, &data->mutex);
+ }
+ vlc_cleanup_pop();
+ vlc_mutex_unlock(&data->mutex);
+
+ return &thread_return_magic;
+}
+
+static void *thread_func_cond_timeout(void *ptr)
+{
+ struct cond_data *data = ptr;
+
+ // Wait until timeout
+ vlc_mutex_lock(&data->mutex);
+ mutex_cleanup_push(&data->mutex);
+
+ vlc_tick_t now = vlc_tick_now();
+ vlc_tick_t deadline = now + VLC_TICK_FROM_MS(25);
+ int ret = vlc_cond_timedwait(&data->cond_1, &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 *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 *thread_func_cond_multi(void *ptr)
+{
+ struct cond_data *data = ptr;
+
+ // Decrement state
+ vlc_mutex_lock(&data->mutex);
+ data->state -= 1;
+ vlc_cond_signal(&data->cond_1);
+ vlc_mutex_unlock(&data->mutex);
+
+ // Wait for state change
+ vlc_mutex_lock(&data->mutex);
+ mutex_cleanup_push(&data->mutex);
+ while(data->state != -1) {
+ vlc_cond_wait(&data->cond_2, &data->mutex);
+ }
+ vlc_cleanup_pop();
+ vlc_mutex_unlock(&data->mutex);
+
+ return &thread_return_magic;
+}
+
+
+int main(void)
+{
+ alarm(4);
+
+ // Test thread creation
+ {
+ vlc_thread_t th;
+ TEST_THREAD_CLONE_MAGIC(&th, thread_func_noop);
+ TEST_THREAD_JOIN_MAGIC(th);
+ }
+
+ // Test thread cancellation
+ {
+ vlc_thread_t th;
+ TEST_THREAD_CLONE_MAGIC(&th, thread_func_loop);
+ TEST_THREAD_CANCEL_JOIN(th);
+ }
+
+ // Test thread cancellation 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 thread waiting on condition
+ {
+ struct cond_data data;
+ data.state = 0;
+ vlc_mutex_init(&data.mutex);
+ vlc_cond_init(&data.cond_1);
+ vlc_cond_init(&data.cond_2);
+
+ vlc_thread_t th;
+ TEST_THREAD_CLONE(&th, thread_func_cond_states, &data);
+
+ // Wait for state 1
+ vlc_mutex_lock(&data.mutex);
+ while(data.state != 1) {
+ vlc_cond_wait(&data.cond_1, &data.mutex);
+ }
+ vlc_mutex_unlock(&data.mutex);
+
+ // Change to state 2
+ vlc_mutex_lock(&data.mutex);
+ data.state = 2;
+ vlc_cond_signal(&data.cond_2);
+ vlc_mutex_unlock(&data.mutex);
+
+ // Wait for thread exit
+ TEST_THREAD_JOIN_MAGIC(th);
+ }
+
+ // Test thread cancelling/cleanup while waiting on condition
+ {
+ struct cond_data data;
+ data.state = 0;
+ vlc_mutex_init(&data.mutex);
+ vlc_cond_init(&data.cond_1);
+ vlc_cond_init(&data.cond_2);
+
+ vlc_thread_t th;
+ TEST_THREAD_CLONE(&th, thread_func_cond_states, &data);
+
+ // Wait for state 1
+ vlc_mutex_lock(&data.mutex);
+ while(data.state != 1) {
+ vlc_cond_wait(&data.cond_1, &data.mutex);
+ }
+ vlc_mutex_unlock(&data.mutex);
+
+ // Cancel thread
+ TEST_THREAD_CANCEL_JOIN(th);
+ }
+
+ // Wait on condition and let it timeout
+ {
+ struct cond_data data;
+ data.state = 0;
+ vlc_mutex_init(&data.mutex);
+ vlc_cond_init(&data.cond_1);
+ vlc_cond_init(&data.cond_2);
+
+ vlc_thread_t th;
+ TEST_THREAD_CLONE(&th, thread_func_cond_timeout, &data);
+
+ // Wait for thread exit
+ TEST_THREAD_JOIN_MAGIC(th);
+ }
+
+ // Test sleeping for delay
+ {
+ 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));
+ }
+
+ // Test sleeping for 0 ticks
+ {
+ vlc_tick_sleep(0);
+ }
+
+ // Test cancelling vlc_tick_sleep
+ {
+ vlc_thread_t th;
+ TEST_THREAD_CLONE_MAGIC(&th, thread_func_tick_sleep);
+ TEST_THREAD_CANCEL_JOIN(th);
+ }
+
+ // Test condition waking several threads
+ {
+ vlc_thread_t threads[20];
+
+ struct cond_data data;
+ vlc_mutex_init(&data.mutex);
+ vlc_cond_init(&data.cond_1);
+ vlc_cond_init(&data.cond_2);
+ data.state = ARRAY_SIZE(threads);
+
+ // Spawn all threads
+ for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) {
+ TEST_THREAD_CLONE(&threads[i], thread_func_cond_multi, &data);
+ }
+
+ // Wait for all threads to decrement state
+ vlc_mutex_lock(&data.mutex);
+ while(data.state > 0) {
+ vlc_cond_wait(&data.cond_1, &data.mutex);
+ }
+ vlc_mutex_unlock(&data.mutex);
+
+ // Broadcast condition to all threads
+ vlc_mutex_lock(&data.mutex);
+ data.state = -1;
+ vlc_cond_broadcast(&data.cond_2);
+ 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]);
+ }
+ }
+
+ return 0;
+}
--
2.21.1 (Apple Git-122.3)
More information about the vlc-devel
mailing list