Linux Audio

Check our new training course

Loading...
/*
 * Copyright (c) 2021 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <ztest.h>
#include <ztest_error_hook.h>

/* Macro declarations */
#define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACKSIZE)
#define SEM_INIT_VAL (0U)
#define SEM_MAX_VAL  (3U)
#define TOTAL_MAX (4U)
#define STACK_NUMS 5
#define PRIO 5
#define LOW_PRIO 8
#define HIGH_PRIO 2

static K_THREAD_STACK_ARRAY_DEFINE(multi_stack_give, STACK_NUMS, STACK_SIZE);
static K_THREAD_STACK_ARRAY_DEFINE(multi_stack_take, STACK_NUMS, STACK_SIZE);

static struct k_thread multi_tid_give[STACK_NUMS];
static struct k_thread multi_tid_take[STACK_NUMS];
static struct k_sem usage_sem, sync_sem, limit_sem, uninit_sem;
static ZTEST_DMEM int flag;
static ZTEST_DMEM int count;

/**
 * @defgroup kernel_sys_sem_tests Semaphore
 * @ingroup all_tests
 * @{
 * @}
 */

static void sem_thread_give_uninit(void *p1, void *p2, void *p3)
{
	ztest_set_fault_valid(true);

	/* use sem without initialse */
	k_sem_give(&uninit_sem);

	ztest_test_fail();
}

static void sem_thread_give(void *p1, void *p2, void *p3)
{
	flag = 1;
	k_sem_give(&usage_sem);
}

static void thread_low_prio_sem_take(void *p1, void *p2, void *p3)
{
	k_sem_take(&usage_sem, K_FOREVER);

	flag = LOW_PRIO;
	k_sem_give(&sync_sem);
}

static void thread_high_prio_sem_take(void *p1, void *p2, void *p3)
{
	k_sem_take(&usage_sem, K_FOREVER);

	flag = HIGH_PRIO;
	k_sem_give(&sync_sem);
}

/**
 * @brief Test semaphore usage with multiple thread
 *
 * @details Using semaphore with some situations
 * - Use a uninitialized semaphore
 * - Use semphore normally
 * - Use semaphore with different priority threads
 *
 * @ingroup kernel_sys_sem_tests
 */
void test_multiple_thread_sem_usage(void)
{
	k_sem_init(&usage_sem, SEM_INIT_VAL, SEM_MAX_VAL);
	k_sem_init(&sync_sem, SEM_INIT_VAL, SEM_MAX_VAL);
	/* Use a semaphore to synchronize processing between threads */
	k_sem_reset(&usage_sem);
	k_thread_create(&multi_tid_give[0], multi_stack_give[0], STACK_SIZE,
			sem_thread_give, NULL, NULL,
			NULL, PRIO, K_USER | K_INHERIT_PERMS,
			K_NO_WAIT);

	k_sem_take(&usage_sem, K_FOREVER);
	zassert_equal(flag, 1, "value != 1");
	zassert_equal(k_sem_count_get(&usage_sem), 0, "sem not be took");

	k_sem_reset(&usage_sem);
	/* Use sem with different priority thread */
	k_thread_create(&multi_tid_take[0], multi_stack_take[0], STACK_SIZE,
			thread_low_prio_sem_take, NULL, NULL,
			NULL, LOW_PRIO, K_USER | K_INHERIT_PERMS,
			K_NO_WAIT);

	k_thread_create(&multi_tid_take[1], multi_stack_take[1], STACK_SIZE,
			thread_high_prio_sem_take, NULL, NULL,
			NULL, HIGH_PRIO, K_USER | K_INHERIT_PERMS,
			K_NO_WAIT);

	k_sleep(K_MSEC(50));

	/* Verify if the high prio thread take sem first */
	k_sem_give(&usage_sem);
	k_sem_take(&sync_sem, K_FOREVER);
	zassert_equal(flag, HIGH_PRIO, "high prio value error");

	k_sem_give(&usage_sem);
	k_sem_take(&sync_sem, K_FOREVER);
	zassert_equal(flag, LOW_PRIO, "low prio value error");

	k_thread_join(&multi_tid_give[0], K_FOREVER);
	k_thread_join(&multi_tid_take[0], K_FOREVER);
	k_thread_join(&multi_tid_take[1], K_FOREVER);

	k_thread_create(&multi_tid_give[1], multi_stack_give[1], STACK_SIZE,
			sem_thread_give_uninit, NULL, NULL,
			NULL, PRIO, K_USER | K_INHERIT_PERMS,
			K_NO_WAIT);
	k_sleep(K_MSEC(20));
	k_thread_join(&multi_tid_give[1], K_FOREVER);
}

static void multi_thread_sem_give(void *p1, void *p2, void *p3)
{
	count++;
	k_sem_give(&limit_sem);

	if (count < TOTAL_MAX)
		zassert_equal(k_sem_count_get(&limit_sem), count, "multi get sem error");
	else
		zassert_equal(k_sem_count_get(&limit_sem), SEM_MAX_VAL, "count > SEM_MAX_VAL");

	k_sem_take(&sync_sem, K_FOREVER);
}

static void multi_thread_sem_take(void *p1, void *p2, void *p3)
{
	k_sem_take(&limit_sem, K_FOREVER);
	count--;

	if (count >= 0)
		zassert_equal(k_sem_count_get(&limit_sem), count, "multi take sem error");
	else
		zassert_equal(k_sem_count_get(&limit_sem), 0, "count < SEM_INIT_VAL");

	k_sem_give(&sync_sem);
}

/**
 * @brief Test max semaphore can be give and take with multiple thread
 *
 * @details
 * - Define and initialize semaphore and thread.
 * - Give sem by multiple threads.
 * - Verify more than max count about semaphore can reach.
 * - Take sem by multiple threads and verify if sem count is correct.
 *
 * @ingroup kernel_sys_sem_tests
 */
void test_multi_thread_sem_limit(void)
{
	k_sem_init(&limit_sem, SEM_INIT_VAL, SEM_MAX_VAL);
	k_sem_init(&sync_sem, SEM_INIT_VAL, SEM_MAX_VAL);

	count = 0;
	for (int i = 1; i <= TOTAL_MAX; i++) {
		k_thread_create(&multi_tid_give[i], multi_stack_give[i], STACK_SIZE,
				multi_thread_sem_give, NULL, NULL, NULL,
				i, K_USER | K_INHERIT_PERMS, K_NO_WAIT);
	}

	k_sleep(K_MSEC(50));

	count = SEM_MAX_VAL;
	for (int i = 1; i <= TOTAL_MAX; i++) {
		k_thread_create(&multi_tid_take[i], multi_stack_take[i], STACK_SIZE,
				multi_thread_sem_take, NULL, NULL, NULL,
				PRIO, K_USER | K_INHERIT_PERMS, K_NO_WAIT);
	}
}

void test_main(void)
{
	k_thread_access_grant(k_current_get(), &usage_sem, &sync_sem, &limit_sem,
			      &multi_tid_give[0], &multi_tid_give[1],
			      &multi_tid_give[2], &multi_tid_give[3],
			      &multi_tid_give[4], &multi_tid_take[4],
			      &multi_tid_take[2], &multi_tid_take[3],
			      &multi_tid_take[0], &multi_tid_take[1],
			      &multi_tid_give[5], &multi_tid_take[5],
			      &multi_stack_take[0], &multi_stack_take[1],
			      &multi_stack_take[3], &multi_stack_take[4],
			      &multi_stack_take[2], &multi_stack_give[0],
			      &multi_stack_give[1], &multi_stack_give[2],
			      &multi_stack_give[3], &multi_stack_give[4]);

	ztest_test_suite(test_kernel_sys_sem,
			 ztest_1cpu_user_unit_test(test_multiple_thread_sem_usage),
			 ztest_1cpu_user_unit_test(test_multi_thread_sem_limit));
	ztest_run_test_suite(test_kernel_sys_sem);
}