#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <getopt.h>
#include <unistd.h>
#include <limits.h>
#include <check.h>
#include <libfyaml.h>
#include <stdatomic.h>
START_TEST(thread_pool_create_destroy)
{
struct fy_thread_pool_cfg cfg;
struct fy_thread_pool *tp;
memset(&cfg, 0, sizeof(cfg));
cfg.flags = 0;
cfg.num_threads = 2;
cfg.userdata = NULL;
tp = fy_thread_pool_create(&cfg);
ck_assert_ptr_ne(tp, NULL);
const struct fy_thread_pool_cfg *got_cfg = fy_thread_pool_get_cfg(tp);
ck_assert_ptr_ne(got_cfg, NULL);
ck_assert_int_eq(got_cfg->num_threads, 2);
int num_threads = fy_thread_pool_get_num_threads(tp);
ck_assert_int_eq(num_threads, 2);
fy_thread_pool_destroy(tp);
}
END_TEST
static void atomic_increment_worker(void *arg)
{
_Atomic(int) *p = arg;
int v, exp_v;
v = atomic_load(p);
for (;;) {
exp_v = v;
if (atomic_compare_exchange_strong(p, &exp_v, v + 1))
return;
v = exp_v;
}
}
START_TEST(thread_reserve_submit_wait)
{
struct fy_thread_pool_cfg cfg;
struct fy_thread_pool *tp;
struct fy_thread *threads[4];
struct fy_thread_work works[4];
_Atomic(int) counter = 0;
unsigned int i;
memset(&cfg, 0, sizeof(cfg));
cfg.flags = 0;
cfg.num_threads = 4;
cfg.userdata = NULL;
tp = fy_thread_pool_create(&cfg);
ck_assert_ptr_ne(tp, NULL);
for (i = 0; i < 4; i++) {
threads[i] = fy_thread_reserve(tp);
ck_assert_ptr_ne(threads[i], NULL);
}
for (i = 0; i < 4; i++) {
works[i].fn = atomic_increment_worker;
works[i].arg = &counter;
fy_thread_submit_work(threads[i], &works[i]);
}
for (i = 0; i < 4; i++) {
fy_thread_wait_work(threads[i]);
}
ck_assert_int_eq(atomic_load(&counter), 4);
for (i = 0; i < 4; i++) {
fy_thread_unreserve(threads[i]);
}
fy_thread_pool_destroy(tp);
}
END_TEST
START_TEST(thread_arg_join)
{
struct fy_thread_pool_cfg cfg;
struct fy_thread_pool *tp;
_Atomic(int) counter = 0;
unsigned int num_tasks = 8;
memset(&cfg, 0, sizeof(cfg));
cfg.flags = 0;
cfg.num_threads = 4;
cfg.userdata = NULL;
tp = fy_thread_pool_create(&cfg);
ck_assert_ptr_ne(tp, NULL);
fy_thread_arg_join(tp, atomic_increment_worker, NULL, &counter, num_tasks);
ck_assert_int_eq(atomic_load(&counter), (int)num_tasks);
fy_thread_pool_destroy(tp);
}
END_TEST
struct sum_arg {
const int *values;
int count;
int result;
};
static void sum_worker(void *arg)
{
struct sum_arg *s = arg;
int i;
int sum = 0;
for (i = 0; i < s->count; i++) {
sum += s->values[i];
}
s->result = sum;
}
START_TEST(thread_arg_array_join)
{
struct fy_thread_pool_cfg cfg;
struct fy_thread_pool *tp;
int values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
struct sum_arg args[2];
int total_sum;
memset(&cfg, 0, sizeof(cfg));
cfg.flags = 0;
cfg.num_threads = 2;
cfg.userdata = NULL;
tp = fy_thread_pool_create(&cfg);
ck_assert_ptr_ne(tp, NULL);
args[0].values = values;
args[0].count = 5;
args[0].result = 0;
args[1].values = values + 5;
args[1].count = 5;
args[1].result = 0;
fy_thread_arg_array_join(tp, sum_worker, NULL, args, sizeof(struct sum_arg), 2);
total_sum = args[0].result + args[1].result;
ck_assert_int_eq(total_sum, 55);
ck_assert_int_eq(args[0].result, 15);
ck_assert_int_eq(args[1].result, 40);
fy_thread_pool_destroy(tp);
}
END_TEST
static void steal_mode_worker(void *arg)
{
_Atomic(int) *p = arg;
int v, exp_v;
int i;
for (i = 0; i < 100; i++) {
v = atomic_load(p);
for (;;) {
exp_v = v;
if (atomic_compare_exchange_strong(p, &exp_v, v + 1))
break;
v = exp_v;
}
}
}
START_TEST(thread_steal_mode)
{
struct fy_thread_pool_cfg cfg;
struct fy_thread_pool *tp;
_Atomic(int) counter = 0;
unsigned int num_tasks = 16;
memset(&cfg, 0, sizeof(cfg));
cfg.flags = FYTPCF_STEAL_MODE;
cfg.num_threads = 4;
cfg.userdata = NULL;
tp = fy_thread_pool_create(&cfg);
ck_assert_ptr_ne(tp, NULL);
fy_thread_arg_join(tp, steal_mode_worker, NULL, &counter, num_tasks);
ck_assert_int_eq(atomic_load(&counter), (int)(num_tasks * 100));
fy_thread_pool_destroy(tp);
}
END_TEST
TCase *libfyaml_case_thread(void)
{
TCase *tc;
tc = tcase_create("thread");
tcase_add_test(tc, thread_pool_create_destroy);
tcase_add_test(tc, thread_reserve_submit_wait);
tcase_add_test(tc, thread_arg_join);
tcase_add_test(tc, thread_arg_array_join);
tcase_add_test(tc, thread_steal_mode);
return tc;
}