1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00
VILLASnode/tests/unit/queue.c

359 lines
8.2 KiB
C
Raw Permalink Normal View History

/** Unit tests for queue
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
2017-04-27 12:56:43 +02:00
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
2017-04-27 12:56:43 +02:00
* 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 General Public License for more details.
*
2017-04-27 12:56:43 +02:00
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
2016-10-19 01:25:52 -04:00
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <pthread.h>
2016-10-06 17:56:55 -04:00
#include <criterion/criterion.h>
2016-10-19 01:25:52 -04:00
#include <criterion/logging.h>
#include <criterion/parameterized.h>
2016-10-06 17:56:55 -04:00
2018-03-26 12:50:15 +02:00
#include <villas/utils.h>
#include <villas/queue.h>
#include <villas/memory.h>
2016-10-06 17:56:55 -04:00
2016-10-19 01:25:52 -04:00
#define SIZE (1 << 10)
2017-03-25 21:23:48 +01:00
static struct queue q = { .state = STATE_DESTROYED };
2016-10-19 01:25:52 -04:00
#if defined(_POSIX_BARRIERS) && _POSIX_BARRIERS > 0
2017-03-29 04:26:16 +02:00
static pthread_barrier_t barrier;
#endif
2017-03-29 04:26:16 +02:00
2016-10-19 01:25:52 -04:00
struct param {
int volatile start;
int thread_count;
int queue_size;
int iter_count;
int batch_size;
void * (*thread_func)(void *);
struct queue queue;
2018-07-02 14:17:50 +02:00
const struct memory_type *memory_type;
2016-10-19 01:25:52 -04:00
};
/** Get thread id as integer
* In contrast to pthread_t which is an opaque type */
#ifdef __linux__
#include <sys/syscall.h>
#endif
uint64_t thread_get_id()
{
#ifdef __MACH__
uint64_t id;
pthread_threadid_np(pthread_self(), &id);
return id;
#elif defined(SYS_gettid)
return (int) syscall(SYS_gettid);
#endif
return -1;
}
/** Sleep, do nothing */
__attribute__((always_inline)) static inline void nop()
{
__asm__("rep nop;");
}
static void * producer(void *ctx)
{
int ret;
2017-03-25 21:23:48 +01:00
struct param *p = ctx;
2016-10-19 01:25:52 -04:00
srand((unsigned) time(0) + thread_get_id());
size_t nops = rand() % 1000;
2016-10-19 01:25:52 -04:00
/** @todo Criterion cr_log() is broken for multi-threaded programs */
//cr_log_info("producer: tid = %lu", thread_get_id());
#ifdef __APPLE__
#define pthread_yield pthread_yield_np
#endif
2016-10-19 01:25:52 -04:00
/* Wait for global start signal */
while (p->start == 0)
pthread_yield();
2016-10-19 01:25:52 -04:00
//cr_log_info("producer: wait for %zd nops", nops);
2016-10-19 01:25:52 -04:00
/* Wait for a random time */
for (size_t i = 0; i != nops; i += 1)
nop();
2016-10-19 01:25:52 -04:00
//cr_log_info("producer: start pushing");
2016-10-19 01:25:52 -04:00
/* Enqueue */
for (unsigned long count = 0; count < p->iter_count; count++) {
do {
ret = queue_push(&p->queue, (void *) count);
pthread_yield();
} while (ret != 1);
}
2016-10-19 01:25:52 -04:00
//cr_log_info("producer: finished");
2016-10-19 01:25:52 -04:00
return NULL;
}
static void * consumer(void *ctx)
{
int ret;
2017-03-25 21:23:48 +01:00
struct param *p = ctx;
2016-10-19 01:25:52 -04:00
srand((unsigned) time(0) + thread_get_id());
size_t nops = rand() % 1000;
2016-10-19 01:25:52 -04:00
//cr_log_info("consumer: tid = %lu", thread_get_id());
/* Wait for global start signal */
while (p->start == 0)
pthread_yield();
2016-10-19 01:25:52 -04:00
//cr_log_info("consumer: wait for %zd nops", nops);
2016-10-19 01:25:52 -04:00
/* Wait for a random time */
for (size_t i = 0; i != nops; i += 1)
nop();
2016-10-19 01:25:52 -04:00
//cr_log_info("consumer: start pulling");
2016-10-19 01:25:52 -04:00
/* Dequeue */
for (unsigned long count = 0; count < p->iter_count; count++) {
void *ptr;
2016-10-19 01:25:52 -04:00
do {
ret = queue_pull(&p->queue, &ptr);
} while (ret != 1);
2016-10-19 01:25:52 -04:00
//cr_log_info("consumer: %lu\n", count);
2016-10-19 01:25:52 -04:00
//cr_assert_eq((intptr_t) ptr, count);
}
2016-10-19 01:25:52 -04:00
//cr_log_info("consumer: finished");
return NULL;
}
#if defined(_POSIX_BARRIERS) && _POSIX_BARRIERS > 0
2016-10-19 01:25:52 -04:00
void * producer_consumer(void *ctx)
{
2017-03-25 21:23:48 +01:00
struct param *p = ctx;
2016-10-19 01:25:52 -04:00
srand((unsigned) time(0) + thread_get_id());
size_t nops = rand() % 1000;
/* Wait for global start signal */
while (p->start == 0)
pthread_yield();
/* Wait for a random time */
for (size_t i = 0; i != nops; i += 1)
nop();
for (int iter = 0; iter < p->iter_count; ++iter) {
2017-03-29 04:26:16 +02:00
pthread_barrier_wait(&barrier);
2016-10-19 01:25:52 -04:00
for (size_t i = 0; i < p->batch_size; i++) {
void *ptr = (void *) (iter * p->batch_size + i);
while (!queue_push(&p->queue, ptr))
pthread_yield(); /* queue full, let other threads proceed */
}
for (size_t i = 0; i < p->batch_size; i++) {
void *ptr;
while (!queue_pull(&p->queue, &ptr))
pthread_yield(); /* queue empty, let other threads proceed */
}
}
return 0;
}
void * producer_consumer_many(void *ctx)
{
2017-03-25 21:23:48 +01:00
struct param *p = ctx;
2016-10-19 01:25:52 -04:00
srand((unsigned) time(0) + thread_get_id());
size_t nops = rand() % 1000;
/* Wait for global start signal */
while (p->start == 0)
pthread_yield();
/* Wait for a random time */
for (size_t i = 0; i != nops; i += 1)
nop();
2016-10-19 01:25:52 -04:00
void *ptrs[p->batch_size];
for (int iter = 0; iter < p->iter_count; ++iter) {
for (size_t i = 0; i < p->batch_size; i++)
ptrs[i] = (void *) (iter * p->batch_size + i);
2017-03-29 04:26:16 +02:00
pthread_barrier_wait(&barrier);
2016-10-19 01:25:52 -04:00
int pushed = 0;
do {
pushed += queue_push_many(&p->queue, &ptrs[pushed], p->batch_size - pushed);
if (pushed != p->batch_size)
pthread_yield(); /* queue full, let other threads proceed */
} while (pushed < p->batch_size);
int pulled = 0;
do {
pulled += queue_pull_many(&p->queue, &ptrs[pulled], p->batch_size - pulled);
if (pulled != p->batch_size)
pthread_yield(); /* queue empty, let other threads proceed */
} while (pulled < p->batch_size);
}
return 0;
}
#endif /* _POSIX_BARRIERS */
2016-10-19 01:25:52 -04:00
Test(queue, single_threaded)
2016-10-06 17:56:55 -04:00
{
2016-10-19 01:25:52 -04:00
int ret;
struct param p = {
.iter_count = 1 << 8,
.queue_size = 1 << 10,
.start = 1 /* we start immeadiatly */
};
2018-07-02 14:17:50 +02:00
ret = queue_init(&p.queue, p.queue_size, &memory_type_heap);
2016-10-19 01:25:52 -04:00
cr_assert_eq(ret, 0, "Failed to create queue");
2016-10-19 01:25:52 -04:00
producer(&p);
consumer(&p);
2016-10-19 01:25:52 -04:00
cr_assert_eq(queue_available(&q), 0);
2016-10-19 01:25:52 -04:00
ret = queue_destroy(&p.queue);
cr_assert_eq(ret, 0, "Failed to create queue");
}
#if defined(_POSIX_BARRIERS) && _POSIX_BARRIERS > 0
2016-10-19 01:25:52 -04:00
ParameterizedTestParameters(queue, multi_threaded)
{
static struct param params[] = {
{
.iter_count = 1 << 12,
.queue_size = 1 << 9,
.thread_count = 32,
.thread_func = producer_consumer_many,
.batch_size = 10,
2018-07-02 14:17:50 +02:00
.memory_type = &memory_type_heap
2016-10-19 01:25:52 -04:00
}, {
.iter_count = 1 << 8,
.queue_size = 1 << 9,
.thread_count = 4,
.thread_func = producer_consumer_many,
.batch_size = 100,
2018-07-02 14:17:50 +02:00
.memory_type = &memory_type_heap
2016-10-19 01:25:52 -04:00
}, {
.iter_count = 1 << 16,
2017-03-29 04:26:16 +02:00
.queue_size = 1 << 14,
2016-10-19 01:25:52 -04:00
.thread_count = 16,
.thread_func = producer_consumer_many,
.batch_size = 100,
2018-07-02 14:17:50 +02:00
.memory_type = &memory_type_heap
2016-10-19 01:25:52 -04:00
}, {
.iter_count = 1 << 8,
.queue_size = 1 << 9,
.thread_count = 4,
.thread_func = producer_consumer_many,
.batch_size = 10,
2018-07-02 14:17:50 +02:00
.memory_type = &memory_type_heap
2016-10-19 01:25:52 -04:00
}, {
.iter_count = 1 << 16,
.queue_size = 1 << 9,
.thread_count = 16,
.thread_func = producer_consumer,
.batch_size = 10,
2018-07-02 14:17:50 +02:00
.memory_type = &memory_hugepage
2016-10-19 01:25:52 -04:00
}
};
2016-10-19 01:25:52 -04:00
return cr_make_param_array(struct param, params, ARRAY_LEN(params));
}
2017-03-29 04:26:16 +02:00
ParameterizedTest(struct param *p, queue, multi_threaded, .timeout = 20)
2016-10-19 01:25:52 -04:00
{
int ret, cycpop;
2016-10-19 01:25:52 -04:00
pthread_t threads[p->thread_count];
2016-10-19 01:25:52 -04:00
p->start = 0;
2018-07-02 14:17:50 +02:00
ret = queue_init(&p->queue, p->queue_size, &memory_type_heap);
2016-10-19 01:25:52 -04:00
cr_assert_eq(ret, 0, "Failed to create queue");
uint64_t start_tsc_time, end_tsc_time;
2017-03-29 04:26:16 +02:00
pthread_barrier_init(&barrier, NULL, p->thread_count);
2016-10-19 01:25:52 -04:00
for (int i = 0; i < p->thread_count; ++i)
2017-03-25 21:23:48 +01:00
pthread_create(&threads[i], NULL, p->thread_func, p);
2016-10-19 01:25:52 -04:00
sleep(0.2);
2016-10-19 01:25:52 -04:00
start_tsc_time = rdtsc();
p->start = 1;
for (int i = 0; i < p->thread_count; ++i)
pthread_join(threads[i], NULL);
2016-10-19 01:25:52 -04:00
end_tsc_time = rdtsc();
cycpop = (end_tsc_time - start_tsc_time) / p->iter_count;
2016-10-19 01:25:52 -04:00
if (cycpop < 400)
cr_log_info("cycles/op: %u\n", cycpop);
else
2017-03-25 21:23:48 +01:00
cr_log_warn("cycles/op are very high (%u). Are you running on a hypervisor?\n", cycpop);
2016-10-19 01:25:52 -04:00
2017-03-25 21:23:48 +01:00
ret = queue_available(&q);
cr_assert_eq(ret, 0);
2016-10-19 01:25:52 -04:00
ret = queue_destroy(&p->queue);
2017-03-25 21:23:48 +01:00
cr_assert_eq(ret, 0, "Failed to destroy queue");
ret = pthread_barrier_destroy(&barrier);
cr_assert_eq(ret, 0, "Failed to destroy barrier");
2016-10-19 01:25:52 -04:00
}
#endif /* _POSIX_BARRIERS */
2016-10-19 01:25:52 -04:00
Test(queue, init_destroy)
{
int ret;
2017-03-25 21:23:48 +01:00
struct queue q = { .state = STATE_DESTROYED };
2018-07-02 14:17:50 +02:00
ret = queue_init(&q, 1024, &memory_type_heap);
2016-10-19 01:25:52 -04:00
cr_assert_eq(ret, 0); /* Should succeed */
2016-10-19 01:25:52 -04:00
ret = queue_destroy(&q);
cr_assert_eq(ret, 0); /* Should succeed */
}