|
|
|
|
Quelle thread.c
Sprache: C
|
|
/*****************************************************************************
* 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>
#include <vlc_threads.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) == 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(void)
{
vlc_thread_t th;
TEST_THREAD_CLONE_MAGIC(&th, thread_func_noop);
TEST_THREAD_JOIN_MAGIC(th);
}
/*
* Test simple thread cancellation
*/
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(void)
{
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(void)
{
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
*/
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);
while (data->state != -1)
vlc_cond_wait(&data->cond_end, &data->mutex);
vlc_mutex_unlock(&data->mutex);
return &thread_return_magic;
}
static void test__cond_broadcast(void)
{
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(void)
{
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);
}
/*
* 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);
// 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_mutex_unlock(&data->mutex);
return &thread_return_magic;
}
static void test__cond_wait_timeout(void)
{
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 latch synchronisation
*/
static void *thread_latch(void *data)
{
vlc_latch_t *l = data;
assert(!vlc_latch_is_ready(l));
vlc_latch_count_down(l, 1);
vlc_latch_wait(l);
assert(vlc_latch_is_ready(l));
return NULL;
}
static void test__latch(void)
{
vlc_latch_t l;
vlc_thread_t th;
vlc_latch_init(&l, 0);
assert(vlc_latch_is_ready(&l));
vlc_latch_wait(&l);
assert(vlc_latch_is_ready(&l));
vlc_latch_init(&l, 5);
assert(!vlc_latch_is_ready(&l));
vlc_latch_count_down(&l, 3);
assert(!vlc_latch_is_ready(&l));
vlc_latch_count_down_and_wait(&l, 2);
assert(vlc_latch_is_ready(&l));
vlc_latch_wait(&l);
assert(vlc_latch_is_ready(&l));
vlc_latch_init(&l, 2);
TEST_THREAD_CLONE(&th, thread_latch, &l);
vlc_latch_count_down_and_wait(&l, 1);
TEST_THREAD_JOIN(th, NULL);
}
/*
* 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(void)
{
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
*/
static void test__vlc_tick_sleep(void)
{
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));
}
/*
* 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_timeout();
test__cond_broadcast();
test__latch();
test__vlc_tick_sleep_cancelation();
test__vlc_tick_sleep();
return 0;
}
¤ Dauer der Verarbeitung: 0.4 Sekunden
¤
*© Formatika GbR, Deutschland
|
|
2026-11-01
|
|
|
|
|
Neuigkeiten |
| Aktuelles |
| Motto des Tages |
|
Open Source Software |
|
|
|
Jenseits des Üblichen ....
|
|
Besucherstatistik |
|
|
| Statistik |
| #Sources=1104842 |
| #Domains=499162 |
|
|