blob: 9a02e0edac444261f19def560576259d6971baff [file] [log] [blame]
/*
* Copyright (c) 2016, 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <zephyr/irq_offload.h>
#include <ztest_error_hook.h>
/* Macro declarations */
#define SEM_INIT_VAL (0U)
#define SEM_MAX_VAL (10U)
#define THREAD_TEST_PRIORITY 0
#define sem_give_from_isr(sema) irq_offload(isr_sem_give, (const void *)sema)
#define sem_take_from_isr(sema) irq_offload(isr_sem_take, (const void *)sema)
#define SEM_TIMEOUT (K_MSEC(100))
#define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
#define TOTAL_THREADS_WAITING (5)
#define SEC2MS(s) ((s) * 1000)
#define QSEC2MS(s) ((s) * 250)
#define expect_k_sem_take(sem, timeout, exp, str) do { \
int _act = k_sem_take((sem), (timeout)); \
int _exp = (exp); \
zassert_equal(_act, _exp, (str), _act, _exp); \
} while (0)
#define expect_k_sem_init(sem, init, max, exp, str) do { \
int _act = k_sem_init((sem), (init), (max)); \
int _exp = (exp); \
zassert_equal(_act, _exp, (str), _act, _exp); \
} while (0)
#define expect_k_sem_count_get(sem, exp, str) do { \
unsigned int _act = k_sem_count_get(sem); \
unsigned int _exp = (exp); \
zassert_equal(_act, _exp, (str), _act, _exp); \
} while (0)
#define expect_k_sem_take_nomsg(sem, timeout, exp) \
expect_k_sem_take((sem), (timeout), (exp), "k_sem_take incorrect return value: %d != %d")
#define expect_k_sem_init_nomsg(sem, init, max, exp) \
expect_k_sem_init((sem), (init), (max), (exp), \
"k_sem_init incorrect return value: %d != %d")
#define expect_k_sem_count_get_nomsg(sem, exp) \
expect_k_sem_count_get((sem), (exp), "k_sem_count_get incorrect return value: %u != %u")
extern void test_sem_give_null(void);
extern void test_sem_init_null(void);
extern void test_sem_take_null(void);
extern void test_sem_reset_null(void);
extern void test_sem_count_get_null(void);
/* global variable for mutual exclusion test */
uint32_t critical_var;
struct timeout_info {
uint32_t timeout;
struct k_sem *sema;
};
/******************************************************************************/
/* Kobject declaration */
K_SEM_DEFINE(simple_sem, SEM_INIT_VAL, SEM_MAX_VAL);
K_SEM_DEFINE(low_prio_sem, SEM_INIT_VAL, SEM_MAX_VAL);
K_SEM_DEFINE(mid_prio_sem, SEM_INIT_VAL, SEM_MAX_VAL);
K_SEM_DEFINE(high_prio_long_sem, SEM_INIT_VAL, SEM_MAX_VAL);
K_SEM_DEFINE(high_prio_sem, SEM_INIT_VAL, SEM_MAX_VAL);
K_SEM_DEFINE(multiple_thread_sem, SEM_INIT_VAL, SEM_MAX_VAL);
K_THREAD_STACK_DEFINE(stack_1, STACK_SIZE);
K_THREAD_STACK_DEFINE(stack_2, STACK_SIZE);
K_THREAD_STACK_DEFINE(stack_3, STACK_SIZE);
K_THREAD_STACK_DEFINE(stack_4, STACK_SIZE);
K_THREAD_STACK_ARRAY_DEFINE(multiple_stack, TOTAL_THREADS_WAITING, STACK_SIZE);
K_PIPE_DEFINE(timeout_info_pipe,
sizeof(struct timeout_info) * TOTAL_THREADS_WAITING, 4);
struct k_thread sem_tid_1, sem_tid_2, sem_tid_3, sem_tid_4;
struct k_thread multiple_tid[TOTAL_THREADS_WAITING];
K_SEM_DEFINE(ksema, SEM_INIT_VAL, SEM_MAX_VAL);
struct k_sem sema, mut_sem;
static K_THREAD_STACK_DEFINE(tstack, STACK_SIZE);
struct k_thread tdata;
/******************************************************************************/
/* Helper functions */
void sem_give_task(void *p1, void *p2, void *p3)
{
k_sem_give((struct k_sem *)p1);
}
void sem_reset_take_task(void *p1, void *p2, void *p3)
{
k_sem_reset((struct k_sem *)p1);
zassert_false(k_sem_take((struct k_sem *)p1, K_FOREVER), NULL);
}
void isr_sem_give(const void *semaphore)
{
k_sem_give((struct k_sem *)semaphore);
}
static void tsema_thread_thread(struct k_sem *psem)
{
/**TESTPOINT: thread-thread sync via sema*/
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
sem_give_task, psem, NULL, NULL,
K_PRIO_PREEMPT(0),
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
expect_k_sem_take_nomsg(psem, K_FOREVER, 0);
/*clean the spawn thread avoid side effect in next TC*/
k_thread_join(tid, K_FOREVER);
}
static void tsema_thread_isr(struct k_sem *psem)
{
/**TESTPOINT: thread-isr sync via sema*/
irq_offload(isr_sem_give, (const void *)psem);
expect_k_sem_take_nomsg(psem, K_FOREVER, 0);
}
void isr_sem_take(const void *semaphore)
{
int ret = k_sem_take((struct k_sem *)semaphore, K_NO_WAIT);
if (ret != 0 && ret != -EBUSY) {
zassert_true(false, "incorrect k_sem_take return: %d", ret);
}
}
void sem_take_timeout_forever_helper(void *p1, void *p2, void *p3)
{
k_sleep(K_MSEC(100));
k_sem_give(&simple_sem);
}
void sem_take_timeout_isr_helper(void *p1, void *p2, void *p3)
{
sem_give_from_isr(&simple_sem);
}
void sem_take_multiple_low_prio_helper(void *p1, void *p2, void *p3)
{
expect_k_sem_take_nomsg(&low_prio_sem, K_FOREVER, 0);
expect_k_sem_take_nomsg(&multiple_thread_sem, K_FOREVER, 0);
k_sem_give(&low_prio_sem);
}
void sem_take_multiple_mid_prio_helper(void *p1, void *p2, void *p3)
{
expect_k_sem_take_nomsg(&mid_prio_sem, K_FOREVER, 0);
expect_k_sem_take_nomsg(&multiple_thread_sem, K_FOREVER, 0);
k_sem_give(&mid_prio_sem);
}
void sem_take_multiple_high_prio_helper(void *p1, void *p2, void *p3)
{
expect_k_sem_take_nomsg(&high_prio_sem, K_FOREVER, 0);
expect_k_sem_take_nomsg(&multiple_thread_sem, K_FOREVER, 0);
k_sem_give(&high_prio_sem);
}
/* First function for mutual exclusion test */
void sem_queue_mutual_exclusion1(void *p1, void *p2, void *p3)
{
for (int i = 0; i < 10000; i++) {
expect_k_sem_take_nomsg(&mut_sem, K_FOREVER, 0);
/* in that function critical section makes critical var +1 */
uint32_t tmp = critical_var;
critical_var += 1;
/* Check that common value was not changed by another thread,
* when semaphore is taken by current thread, and no other
* thread can enter the critical section
*/
zassert_true(critical_var == tmp + 1, NULL);
k_sem_give(&mut_sem);
}
}
/* Second function for mutual exclusion test */
void sem_queue_mutual_exclusion2(void *p1, void *p2, void *p3)
{
for (int i = 0; i < 10000; i++) {
expect_k_sem_take_nomsg(&mut_sem, K_FOREVER, 0);
/* in that function critical section makes critical var 0 */
uint32_t tmp = critical_var;
critical_var -= 1;
/* Check that common value was not changed by another thread,
* when semaphore is taken by current thread, and no other
* thread can enter the critical section
*/
zassert_true(critical_var == tmp - 1, NULL);
k_sem_give(&mut_sem);
}
}
void sem_take_multiple_high_prio_long_helper(void *p1, void *p2, void *p3)
{
expect_k_sem_take_nomsg(&high_prio_long_sem, K_FOREVER, 0);
expect_k_sem_take_nomsg(&multiple_thread_sem, K_FOREVER, 0);
k_sem_give(&high_prio_long_sem);
}
/**
* @ingroup kernel_semaphore_tests
* @{
*/
/**
* @brief Test semaphore defined at compile time
* @details
* - Get the semaphore count.
* - Verify the semaphore count equals to initialized value.
* @ingroup kernel_semaphore_tests
* @see k_sem_count_get()
*/
void test_k_sem_define(void)
{
/* verify the semaphore count equals to initialized value */
expect_k_sem_count_get(&simple_sem, SEM_INIT_VAL,
"semaphore initialized failed at compile time"
"- got %u, expected %u");
}
/**
* @brief Test synchronization of threads with semaphore
* @see k_sem_init(), #K_SEM_DEFINE(x)
*/
void test_sem_thread2thread(void)
{
/**TESTPOINT: test k_sem_init sema*/
expect_k_sem_init_nomsg(&sema, SEM_INIT_VAL, SEM_MAX_VAL, 0);
tsema_thread_thread(&sema);
/**TESTPOINT: test K_SEM_DEFINE sema*/
tsema_thread_thread(&ksema);
}
/**
* @brief Test synchronization between thread and irq
* @see k_sem_init(), #K_SEM_DEFINE(x)
*/
void test_sem_thread2isr(void)
{
/**TESTPOINT: test k_sem_init sema*/
expect_k_sem_init_nomsg(&sema, SEM_INIT_VAL, SEM_MAX_VAL, 0);
tsema_thread_isr(&sema);
/**TESTPOINT: test K_SEM_DEFINE sema*/
tsema_thread_isr(&ksema);
}
/**
* @brief Test semaphore initialization at running time
* @details
* - Initialize a semaphore with valid count and max limit.
* - Initialize a semaphore with invalid max limit.
* - Initialize a semaphore with invalid count.
* @ingroup kernel_semaphore_tests
*/
void test_k_sem_init(void)
{
/* initialize a semaphore with valid count and max limit */
expect_k_sem_init_nomsg(&sema, SEM_INIT_VAL, SEM_MAX_VAL, 0);
k_sem_reset(&sema);
/* initialize a semaphore with invalid max limit */
expect_k_sem_init_nomsg(&sema, SEM_INIT_VAL, 0, -EINVAL);
/* initialize a semaphore with invalid count */
expect_k_sem_init_nomsg(&sema, SEM_MAX_VAL + 1, SEM_MAX_VAL, -EINVAL);
}
/**
* @brief Test k_sem_reset() API
* @see k_sem_reset()
*/
void test_sem_reset(void)
{
expect_k_sem_init_nomsg(&sema, SEM_INIT_VAL, SEM_MAX_VAL, 0);
expect_k_sem_count_get_nomsg(&sema, 0);
k_sem_give(&sema);
expect_k_sem_count_get_nomsg(&sema, 1);
k_sem_reset(&sema);
expect_k_sem_count_get_nomsg(&sema, 0);
/**TESTPOINT: semaphore take return -EBUSY*/
expect_k_sem_take_nomsg(&sema, K_NO_WAIT, -EBUSY);
expect_k_sem_count_get_nomsg(&sema, 0);
/**TESTPOINT: semaphore take return -EAGAIN*/
expect_k_sem_take_nomsg(&sema, SEM_TIMEOUT, -EAGAIN);
expect_k_sem_count_get_nomsg(&sema, 0);
k_sem_give(&sema);
expect_k_sem_count_get_nomsg(&sema, 1);
expect_k_sem_take_nomsg(&sema, K_FOREVER, 0);
expect_k_sem_count_get_nomsg(&sema, 0);
}
void test_sem_reset_waiting(void)
{
int32_t ret_value;
k_sem_reset(&simple_sem);
/* create a new thread. It will reset the semaphore in 1ms
* then wait for us.
*/
k_tid_t tid = k_thread_create(&sem_tid_1, stack_1, STACK_SIZE,
sem_reset_take_task, &simple_sem, NULL, NULL,
K_PRIO_PREEMPT(0), K_USER | K_INHERIT_PERMS,
K_MSEC(1));
/* Take semaphore and wait for the abort. */
ret_value = k_sem_take(&simple_sem, K_FOREVER);
zassert_true(ret_value == -EAGAIN, "k_sem_take not aborted: %d",
ret_value);
/* ensure the semaphore is still functional after. */
k_sem_give(&simple_sem);
k_thread_join(tid, K_FOREVER);
}
/**
* @brief Test k_sem_count_get() API
* @see k_sem_count_get()
*/
void test_sem_count_get(void)
{
expect_k_sem_init_nomsg(&sema, SEM_INIT_VAL, SEM_MAX_VAL, 0);
/**TESTPOINT: semaphore count get upon init*/
expect_k_sem_count_get_nomsg(&sema, SEM_INIT_VAL);
k_sem_give(&sema);
/**TESTPOINT: sem count get after give*/
expect_k_sem_count_get_nomsg(&sema, SEM_INIT_VAL + 1);
expect_k_sem_take_nomsg(&sema, K_FOREVER, 0);
/**TESTPOINT: sem count get after take*/
for (int i = 0; i < SEM_MAX_VAL; i++) {
expect_k_sem_count_get_nomsg(&sema, SEM_INIT_VAL + i);
k_sem_give(&sema);
}
/**TESTPOINT: semaphore give above limit*/
k_sem_give(&sema);
expect_k_sem_count_get_nomsg(&sema, SEM_MAX_VAL);
}
/**
* @brief Test whether a semaphore can be given by an ISR
* @details
* - Reset an initialized semaphore's count to zero
* - Create a loop, in each loop, do follow steps
* - Give the semaphore from an ISR
* - Get the semaphore's count
* - Verify whether the semaphore's count as expected
* @ingroup kernel_semaphore_tests
* @see k_sem_give()
*/
void test_sem_give_from_isr(void)
{
/*
* Signal the semaphore several times from an ISR. After each signal,
* check the signal count.
*/
k_sem_reset(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, 0);
for (int i = 0; i < 5; i++) {
sem_give_from_isr(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, i + 1);
}
}
/**
* @brief Test semaphore count when given by thread
* @details
* - Reset an initialized semaphore's count to zero
* - Create a loop, in each loop, do follow steps
* - Give the semaphore from a thread
* - Get the semaphore's count
* - Verify whether the semaphore's count as expected
* @ingroup kernel_semaphore_tests
* @see k_sem_give()
*/
void test_sem_give_from_thread(void)
{
/*
* Signal the semaphore several times from a task. After each signal,
* check the signal count.
*/
k_sem_reset(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, 0);
for (int i = 0; i < 5; i++) {
k_sem_give(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, i + 1);
}
}
/**
* @brief Test if k_sem_take() decreases semaphore count
* @see k_sem_take()
*/
void test_sem_take_no_wait(void)
{
/*
* Test the semaphore without wait. Check the signal count after each
* attempt (it should be decrementing by 1 each time).
*/
k_sem_reset(&simple_sem);
for (int i = 0; i < 5; i++) {
k_sem_give(&simple_sem);
}
for (int i = 4; i >= 0; i--) {
expect_k_sem_take_nomsg(&simple_sem, K_NO_WAIT, 0);
expect_k_sem_count_get_nomsg(&simple_sem, i);
}
}
/**
* @brief Test k_sem_take() when there is no semaphore to take
* @see k_sem_take()
*/
void test_sem_take_no_wait_fails(void)
{
/*
* Test the semaphore without wait. Check the signal count after each
* attempt (it should be always zero).
*/
k_sem_reset(&simple_sem);
for (int i = 4; i >= 0; i--) {
expect_k_sem_take_nomsg(&simple_sem, K_NO_WAIT, -EBUSY);
expect_k_sem_count_get_nomsg(&simple_sem, 0);
}
}
/**
* @brief Test a semaphore take operation with an unavailable semaphore
* @details
* - Reset the semaphore's count to zero, let it unavailable.
* - Take an unavailable semaphore and wait it until timeout.
* @ingroup kernel_semaphore_tests
* @see k_sem_take()
*/
void test_sem_take_timeout_fails(void)
{
/*
* Test the semaphore with timeout without a k_sem_give.
*/
k_sem_reset(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, 0);
/* take an unavailable semaphore and wait it until timeout */
for (int i = 4; i >= 0; i--) {
expect_k_sem_take_nomsg(&simple_sem, SEM_TIMEOUT, -EAGAIN);
}
}
/**
* @brief Test the semaphore take operation with specified timeout
* @details
* - Create a new thread, it will give semaphore.
* - Reset the semaphore's count to zero.
* - Take semaphore and wait it given by other threads in specified timeout.
* @ingroup kernel_semaphore_tests
* @see k_sem_take()
*/
void test_sem_take_timeout(void)
{
/*
* Signal the semaphore upon which the other thread is waiting.
* The thread (which is at a lower priority) will cause simple_sem
* to be signalled, thus waking up this task.
*/
/* create a new thread, it will give semaphore */
k_thread_create(&sem_tid_1, stack_1, STACK_SIZE,
sem_give_task, &simple_sem, NULL, NULL,
K_PRIO_PREEMPT(0), K_USER | K_INHERIT_PERMS,
K_FOREVER);
k_sem_reset(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, 0);
k_thread_start(&sem_tid_1);
/* Take semaphore and wait it given by other threads
* in specified timeout
*/
expect_k_sem_take_nomsg(&simple_sem, SEM_TIMEOUT, 0);
k_thread_join(&sem_tid_1, K_FOREVER);
}
/**
* @brief Test the semaphore take operation with forever wait
* @details
* - Create a new thread, it will give semaphore.
* - Reset the semaphore's count to zero.
* - Take semaphore, wait it given by other thread forever until it's available.
* @ingroup kernel_semaphore_tests
* @see k_sem_take()
*/
void test_sem_take_timeout_forever(void)
{
/*
* Signal the semaphore upon which the another thread is waiting. The
* thread (which is at a lower priority) will cause simple_sem
* to be signalled, thus waking this task.
*/
k_thread_create(&sem_tid_1, stack_1, STACK_SIZE,
sem_take_timeout_forever_helper, NULL, NULL, NULL,
K_PRIO_PREEMPT(0), K_USER | K_INHERIT_PERMS,
K_NO_WAIT);
k_sem_reset(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, 0);
/* Take semaphore and wait it given by
* other threads forever until it's available
*/
expect_k_sem_take_nomsg(&simple_sem, K_FOREVER, 0);
k_thread_join(&sem_tid_1, K_FOREVER);
}
/**
* @brief Test k_sem_take() with timeout in ISR context
* @see k_sem_take()
*/
void test_sem_take_timeout_isr(void)
{
/*
* Signal the semaphore upon which the another thread is waiting. The
* thread (which is at a lower priority) will cause simple_sem
* to be signalled, thus waking this task.
*/
k_thread_create(&sem_tid_1, stack_1, STACK_SIZE,
sem_take_timeout_isr_helper, NULL, NULL, NULL,
K_PRIO_PREEMPT(0), 0, K_NO_WAIT);
k_sem_reset(&simple_sem);
expect_k_sem_take_nomsg(&simple_sem, SEM_TIMEOUT, 0);
k_thread_join(&sem_tid_1, K_FOREVER);
}
/**
* @brief Test semaphore take operation by multiple threads
* @ingroup kernel_semaphore_tests
* @see k_sem_take()
*/
void test_sem_take_multiple(void)
{
k_sem_reset(&multiple_thread_sem);
expect_k_sem_count_get_nomsg(&multiple_thread_sem, 0);
/*
* Signal the semaphore upon which the another thread is waiting.
* The thread (which is at a lower priority) will cause simple_sem
* to be signalled, thus waking this task.
*/
k_thread_create(&sem_tid_1, stack_1, STACK_SIZE,
sem_take_multiple_low_prio_helper,
NULL, NULL, NULL,
K_PRIO_PREEMPT(3), K_USER | K_INHERIT_PERMS,
K_NO_WAIT);
k_thread_create(&sem_tid_2, stack_2, STACK_SIZE,
sem_take_multiple_mid_prio_helper,
NULL, NULL, NULL,
K_PRIO_PREEMPT(2), K_USER | K_INHERIT_PERMS,
K_NO_WAIT);
k_thread_create(&sem_tid_3, stack_3, STACK_SIZE,
sem_take_multiple_high_prio_long_helper,
NULL, NULL, NULL,
K_PRIO_PREEMPT(1), K_USER | K_INHERIT_PERMS,
K_NO_WAIT);
/* Create another high priority thread, the same priority with sem_tid_3
* sem_tid_3 and sem_tid_4 are the same highest priority,
* but the waiting time of sem_tid_3 is longer than sem_tid_4.
* If some threads are the same priority, the sem given operation
* should be decided according to waiting time.
* That thread is necessary to test if a sem is available,
* it should be given to the highest priority and longest waiting thread
*/
k_thread_create(&sem_tid_4, stack_4, STACK_SIZE,
sem_take_multiple_high_prio_helper, NULL, NULL,
NULL, K_PRIO_PREEMPT(1), K_USER | K_INHERIT_PERMS,
K_NO_WAIT);
/* time for those 4 threads to complete */
k_sleep(K_MSEC(20));
/* Let these threads proceed to take the multiple_sem
* make thread 1 to 3 waiting on multiple_thread_sem
*/
k_sem_give(&high_prio_long_sem);
k_sem_give(&mid_prio_sem);
k_sem_give(&low_prio_sem);
/* Delay 100ms to make sem_tid_4 waiting on multiple_thread_sem,
* then waiting time of sem_tid_4 is shorter than sem_tid_3
*/
k_sleep(K_MSEC(100));
k_sem_give(&high_prio_sem);
k_sleep(K_MSEC(20));
/* enable the high prio and long waiting thread sem_tid_3 to run */
k_sem_give(&multiple_thread_sem);
k_sleep(K_MSEC(200));
/* check which threads completed */
expect_k_sem_count_get(&high_prio_long_sem, 1,
"High priority and long waiting thread "
"don't get the sem: %u != %u");
expect_k_sem_count_get(&high_prio_sem, 0U,
"High priority thread shouldn't get the sem: %u != %u");
expect_k_sem_count_get(&mid_prio_sem, 0U,
"Medium priority threads shouldn't have executed: %u != %u");
expect_k_sem_count_get(&low_prio_sem, 0U,
"Low priority threads shouldn't have executed: %u != %u");
/* enable the high prio thread sem_tid_4 to run */
k_sem_give(&multiple_thread_sem);
k_sleep(K_MSEC(200));
/* check which threads completed */
expect_k_sem_count_get(&high_prio_long_sem, 1U,
"High priority and long waiting thread executed again: %u != %u");
expect_k_sem_count_get(&high_prio_sem, 1U,
"Higher priority thread did not get the sem: %u != %u");
expect_k_sem_count_get(&mid_prio_sem, 0U,
"Medium priority thread shouldn't get the sem: %u != %u");
expect_k_sem_count_get(&low_prio_sem, 0U,
"Low priority thread shouldn't get the sem: %u != %u");
/* enable the mid prio thread sem_tid_2 to run */
k_sem_give(&multiple_thread_sem);
k_sleep(K_MSEC(200));
/* check which threads completed */
expect_k_sem_count_get(&high_prio_long_sem, 1U,
"High priority and long waiting thread executed again: %u != %u");
expect_k_sem_count_get(&high_prio_sem, 1U,
"High priority thread executed again: %u != %u");
expect_k_sem_count_get(&mid_prio_sem, 1U,
"Medium priority thread did not get the sem: %u != %u");
expect_k_sem_count_get(&low_prio_sem, 0U,
"Low priority thread did not get the sem: %u != %u");
/* enable the low prio thread(thread_1) to run */
k_sem_give(&multiple_thread_sem);
k_sleep(K_MSEC(200));
/* check the thread completed */
expect_k_sem_count_get(&high_prio_long_sem, 1U,
"High priority and long waiting thread executed again: %u != %u");
expect_k_sem_count_get(&high_prio_sem, 1U,
"High priority thread executed again: %u != %u");
expect_k_sem_count_get(&mid_prio_sem, 1U,
"Mid priority thread executed again: %u != %u");
expect_k_sem_count_get(&low_prio_sem, 1U,
"Low priority thread did not get the sem: %u != %u");
}
/**
* @brief Test the max value a semaphore can be given and taken
* @details
* - Reset an initialized semaphore's count to zero.
* - Give the semaphore by a thread and verify the semaphore's count is
* as expected.
* - Verify the max count a semaphore can reach.
* - Take the semaphore by a thread and verify the semaphore's count is
* as expected.
* - Verify the max times a semaphore can be taken.
* @ingroup kernel_semaphore_tests
* @see k_sem_count_get(), k_sem_give()
*/
void test_k_sem_correct_count_limit(void)
{
/* reset an initialized semaphore's count to zero */
k_sem_reset(&simple_sem);
expect_k_sem_count_get(&simple_sem, 0U, "k_sem_reset failed: %u != %u");
/* Give the semaphore by a thread and verify the semaphore's
* count is as expected
*/
for (int i = 1; i <= SEM_MAX_VAL; i++) {
k_sem_give(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, i);
}
/* Verify the max count a semaphore can reach
* continue to run k_sem_give,
* the count of simple_sem will not increase anymore
*/
for (int i = 0; i < 5; i++) {
k_sem_give(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, SEM_MAX_VAL);
}
/* Take the semaphore by a thread and verify the semaphore's
* count is as expected
*/
for (int i = SEM_MAX_VAL - 1; i >= 0; i--) {
expect_k_sem_take_nomsg(&simple_sem, K_NO_WAIT, 0);
expect_k_sem_count_get_nomsg(&simple_sem, i);
}
/* Verify the max times a semaphore can be taken
* continue to run k_sem_take, simple_sem can not be taken and
* it's count will be zero
*/
for (int i = 0; i < 5; i++) {
expect_k_sem_take_nomsg(&simple_sem, K_NO_WAIT, -EBUSY);
expect_k_sem_count_get_nomsg(&simple_sem, 0U);
}
}
/**
* @brief Test semaphore give and take and its count from ISR
* @see k_sem_give()
*/
void test_sem_give_take_from_isr(void)
{
k_sem_reset(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, 0U);
/* give semaphore from an isr and do a check for the count */
for (int i = 0; i < SEM_MAX_VAL; i++) {
sem_give_from_isr(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, i + 1);
}
/* take semaphore from an isr and do a check for the count */
for (int i = SEM_MAX_VAL; i > 0; i--) {
sem_take_from_isr(&simple_sem);
expect_k_sem_count_get_nomsg(&simple_sem, i - 1);
}
}
/**
* @}
*/
void sem_multiple_threads_wait_helper(void *p1, void *p2, void *p3)
{
/* get blocked until the test thread gives the semaphore */
expect_k_sem_take_nomsg(&multiple_thread_sem, K_FOREVER, 0);
/* inform the test thread that this thread has got multiple_thread_sem*/
k_sem_give(&simple_sem);
}
/**
* @brief Test multiple semaphore take and give with wait
* @ingroup kernel_semaphore_tests
* @see k_sem_take(), k_sem_give()
*/
void test_sem_multiple_threads_wait(void)
{
k_sem_reset(&simple_sem);
k_sem_reset(&multiple_thread_sem);
/* Verify a wait q that has been emptied / reset
* correctly by running twice.
*/
for (int repeat_count = 0; repeat_count < 2; repeat_count++) {
for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
k_thread_create(&multiple_tid[i],
multiple_stack[i], STACK_SIZE,
sem_multiple_threads_wait_helper,
NULL, NULL, NULL,
K_PRIO_PREEMPT(1),
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
}
/* giving time for the other threads to execute */
k_sleep(K_MSEC(500));
/* give the semaphores */
for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
k_sem_give(&multiple_thread_sem);
}
/* giving time for the other threads to execute */
k_sleep(K_MSEC(500));
/* check if all the threads are done. */
for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
expect_k_sem_take(&simple_sem, K_FOREVER, 0,
"Some of the threads did not get multiple_thread_sem: %d != %d");
}
expect_k_sem_count_get_nomsg(&simple_sem, 0U);
expect_k_sem_count_get_nomsg(&multiple_thread_sem, 0U);
}
}
/**
* @brief Test semaphore timeout period
* @ingroup kernel_semaphore_tests
* @see k_sem_take(), k_sem_give(), k_sem_reset()
*/
void test_sem_measure_timeouts(void)
{
int64_t start_ticks, end_ticks, diff_ticks;
k_sem_reset(&simple_sem);
/* with timeout of 1 sec */
start_ticks = k_uptime_get();
expect_k_sem_take_nomsg(&simple_sem, K_SECONDS(1), -EAGAIN);
end_ticks = k_uptime_get();
diff_ticks = end_ticks - start_ticks;
zassert_true((diff_ticks >= SEC2MS(1)),
"k_sem_take returned too early: %lld < %d",
diff_ticks, SEC2MS(1));
/* This subtest could fail spuriously if we happened to run the below right as
* a tick occurred. Unfortunately, we cannot actually fix this, because on some
* emulated platforms time does not advance while running the cpu, so
* if we spin and wait for a tick boundary we'll spin forever.
* The best we can do is hope that k_busy_wait finishes just after a tick boundary.
*/
k_busy_wait(1);
start_ticks = k_uptime_get();
expect_k_sem_take_nomsg(&simple_sem, K_NO_WAIT, -EBUSY);
end_ticks = k_uptime_get();
diff_ticks = end_ticks - start_ticks;
zassert_true(end_ticks >= start_ticks,
"time went backwards: %lld -> %lld",
start_ticks, end_ticks);
zassert_true(diff_ticks >= 0,
"k_sem_take took too long: %lld != 0",
diff_ticks);
}
void sem_measure_timeout_from_thread_helper(void *p1, void *p2, void *p3)
{
/* first sync the 2 threads */
k_sem_give(&simple_sem);
/* give the semaphore */
k_sem_give(&multiple_thread_sem);
}
/**
* @brief Test timeout of semaphore from thread
* @ingroup kernel_semaphore_tests
* @see k_sem_give(), k_sem_reset(), k_sem_take()
*/
void test_sem_measure_timeout_from_thread(void)
{
int64_t start_ticks, end_ticks, diff_ticks;
k_sem_reset(&simple_sem);
k_sem_reset(&multiple_thread_sem);
/* give a semaphore from a thread and calculate the time taken */
k_thread_create(&sem_tid_1, stack_1, STACK_SIZE,
sem_measure_timeout_from_thread_helper,
NULL, NULL, NULL,
K_PRIO_PREEMPT(3), 0, K_NO_WAIT);
/* first sync the 2 threads */
expect_k_sem_take_nomsg(&simple_sem, K_FOREVER, 0);
/* with timeout of 1 sec */
start_ticks = k_uptime_get();
expect_k_sem_take_nomsg(&multiple_thread_sem, K_SECONDS(1), 0);
end_ticks = k_uptime_get();
diff_ticks = end_ticks - start_ticks;
zassert_true((diff_ticks < SEC2MS(1)),
"k_sem_take took too long: %d >= %d",
diff_ticks, SEC2MS(1));
}
void sem_multiple_take_and_timeouts_helper(void *p1, void *p2, void *p3)
{
int timeout = POINTER_TO_INT(p1);
int64_t start_ticks, end_ticks, diff_ticks;
size_t bytes_written;
start_ticks = k_uptime_get();
expect_k_sem_take_nomsg(&simple_sem, K_MSEC(timeout), -EAGAIN);
end_ticks = k_uptime_get();
diff_ticks = end_ticks - start_ticks;
zassert_true(diff_ticks >= timeout,
"time mismatch - expected at least %d, got %lld",
timeout, diff_ticks);
k_pipe_put(&timeout_info_pipe, &timeout, sizeof(int),
&bytes_written, sizeof(int), K_FOREVER);
}
/**
* @brief Test multiple semaphore take with timeouts
* @ingroup kernel_semaphore_tests
* @see k_sem_take(), k_sem_reset()
*/
void test_sem_multiple_take_and_timeouts(void)
{
if (IS_ENABLED(CONFIG_KERNEL_COHERENCE)) {
ztest_test_skip();
}
static uint32_t timeout;
size_t bytes_read;
k_sem_reset(&simple_sem);
/* Multiple threads timeout and the sequence in which it times out
* is pushed into a pipe and checked later on.
*/
for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
k_thread_create(&multiple_tid[i],
multiple_stack[i], STACK_SIZE,
sem_multiple_take_and_timeouts_helper,
INT_TO_POINTER(QSEC2MS(i + 1)), NULL, NULL,
K_PRIO_PREEMPT(1), 0, K_NO_WAIT);
}
for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
k_pipe_get(&timeout_info_pipe, &timeout, sizeof(int),
&bytes_read, sizeof(int), K_FOREVER);
zassert_equal(timeout, QSEC2MS(i + 1),
"timeout did not occur properly: %d != %d",
timeout, QSEC2MS(i + 1));
}
/* cleanup */
for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
k_thread_join(&multiple_tid[i], K_FOREVER);
}
}
void sem_multi_take_timeout_diff_sem_helper(void *p1, void *p2, void *p3)
{
int timeout = POINTER_TO_INT(p1);
struct k_sem *sema = p2;
int64_t start_ticks, end_ticks, diff_ticks;
size_t bytes_written;
struct timeout_info info = {
.timeout = timeout,
.sema = sema
};
start_ticks = k_uptime_get();
expect_k_sem_take_nomsg(sema, K_MSEC(timeout), -EAGAIN);
end_ticks = k_uptime_get();
diff_ticks = end_ticks - start_ticks;
zassert_true(diff_ticks >= timeout,
"time mismatch - expected at least %d, got %lld",
timeout, diff_ticks);
k_pipe_put(&timeout_info_pipe, &info, sizeof(struct timeout_info),
&bytes_written, sizeof(struct timeout_info), K_FOREVER);
}
/**
* @brief Test sequence of multiple semaphore timeouts
* @ingroup kernel_semaphore_tests
* @see k_sem_take(), k_sem_reset()
*/
void test_sem_multi_take_timeout_diff_sem(void)
{
if (IS_ENABLED(CONFIG_KERNEL_COHERENCE)) {
ztest_test_skip();
}
size_t bytes_read;
struct timeout_info seq_info[] = {
{ SEC2MS(2), &simple_sem },
{ SEC2MS(1), &multiple_thread_sem },
{ SEC2MS(3), &simple_sem },
{ SEC2MS(5), &multiple_thread_sem },
{ SEC2MS(4), &simple_sem },
};
static struct timeout_info retrieved_info;
k_sem_reset(&simple_sem);
k_sem_reset(&multiple_thread_sem);
/* Multiple threads timeout on different semaphores and the sequence
* in which it times out is pushed into a pipe and checked later on.
*/
for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
k_thread_create(&multiple_tid[i],
multiple_stack[i], STACK_SIZE,
sem_multi_take_timeout_diff_sem_helper,
INT_TO_POINTER(seq_info[i].timeout),
seq_info[i].sema, NULL,
K_PRIO_PREEMPT(1), 0, K_NO_WAIT);
}
for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
k_pipe_get(&timeout_info_pipe,
&retrieved_info,
sizeof(struct timeout_info),
&bytes_read,
sizeof(struct timeout_info),
K_FOREVER);
zassert_true(retrieved_info.timeout == SEC2MS(i + 1),
"timeout did not occur properly");
}
}
/**
* @brief Test thread mutual exclusion by semaphore
* @details Test is using to see how mutual exclusion is made by semaphore
* Made two threads, with two functions which use common variable.
* That variable is a critical section and can't be changed by two threads
* at the same time.
* @ingroup kernel_semaphore_tests
*/
void test_sem_queue_mutual_exclusion(void)
{
critical_var = 0;
expect_k_sem_init_nomsg(&mut_sem, 0, 1, 0);
k_thread_create(&sem_tid_1, stack_1, STACK_SIZE,
sem_queue_mutual_exclusion1, NULL, NULL,
NULL, 1, 0,
K_NO_WAIT);
k_thread_create(&sem_tid_2, stack_2, STACK_SIZE,
sem_queue_mutual_exclusion2, NULL, NULL,
NULL, 1, 0,
K_NO_WAIT);
k_sleep(K_MSEC(100));
k_sem_give(&mut_sem);
}
#ifdef CONFIG_USERSPACE
static void thread_sem_give_null(void *p1, void *p2, void *p3)
{
ztest_set_fault_valid(true);
k_sem_give(NULL);
/* should not go here*/
ztest_test_fail();
}
/**
* @brief Test k_sem_give() API
*
* @details Create a thread and set k_sem_give() input to NULL
*
* @ingroup kernel_semaphore_tests
*
* @see k_sem_give()
*/
void test_sem_give_null(void)
{
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
(k_thread_entry_t)thread_sem_give_null,
NULL, NULL, NULL,
K_PRIO_PREEMPT(THREAD_TEST_PRIORITY),
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
k_thread_join(tid, K_FOREVER);
}
#else
void test_sem_give_null(void)
{
/* For those platform not support userspace, we skip it. */
ztest_test_skip();
}
#endif
#ifdef CONFIG_USERSPACE
static void thread_sem_init_null(void *p1, void *p2, void *p3)
{
ztest_set_fault_valid(true);
k_sem_init(NULL, 0, 1);
/* should not go here*/
ztest_test_fail();
}
/**
* @brief Test k_sem_init() API
*
* @details Create a thread and set k_sem_init() input to NULL
*
* @ingroup kernel_semaphore_tests
*
* @see k_sem_init()
*/
void test_sem_init_null(void)
{
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
(k_thread_entry_t)thread_sem_init_null,
NULL, NULL, NULL,
K_PRIO_PREEMPT(THREAD_TEST_PRIORITY),
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
k_thread_join(tid, K_FOREVER);
}
#else
void test_sem_init_null(void)
{
/* For those platform not support userspace, we skip it. */
ztest_test_skip();
}
#endif
#ifdef CONFIG_USERSPACE
static void thread_sem_take_null(void *p1, void *p2, void *p3)
{
ztest_set_fault_valid(true);
k_sem_take(NULL, K_MSEC(1));
/* should not go here*/
ztest_test_fail();
}
/**
* @brief Test k_sem_take() API
*
* @details Create a thread and set k_sem_take() input to NULL
*
* @ingroup kernel_semaphore_tests
*
* @see k_sem_take()
*/
void test_sem_take_null(void)
{
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
(k_thread_entry_t)thread_sem_take_null,
NULL, NULL, NULL,
K_PRIO_PREEMPT(THREAD_TEST_PRIORITY),
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
k_thread_join(tid, K_FOREVER);
}
#else
void test_sem_take_null(void)
{
/* For those platform not support userspace, we skip it. */
ztest_test_skip();
}
#endif
#ifdef CONFIG_USERSPACE
static void thread_sem_reset_null(void *p1, void *p2, void *p3)
{
ztest_set_fault_valid(true);
k_sem_reset(NULL);
/* should not go here*/
ztest_test_fail();
}
/**
* @brief Test k_sem_reset() API
*
* @details Create a thread and set k_sem_reset() input to NULL
*
* @ingroup kernel_semaphore_tests
*
* @see k_sem_reset()
*/
void test_sem_reset_null(void)
{
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
(k_thread_entry_t)thread_sem_reset_null,
NULL, NULL, NULL,
K_PRIO_PREEMPT(THREAD_TEST_PRIORITY),
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
k_thread_join(tid, K_FOREVER);
}
#else
void test_sem_reset_null(void)
{
/* For those platform not support userspace, we skip it. */
ztest_test_skip();
}
#endif
#ifdef CONFIG_USERSPACE
static void thread_sem_count_get_null(void *p1, void *p2, void *p3)
{
ztest_set_fault_valid(true);
k_sem_count_get(NULL);
/* should not go here*/
ztest_test_fail();
}
/**
* @brief Test k_sem_count_get() API
*
* @details Create a thread and set k_sem_count_get() input to NULL
*
* @ingroup kernel_semaphore_tests
*
* @see k_sem_count_get()
*/
void test_sem_count_get_null(void)
{
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
(k_thread_entry_t)thread_sem_count_get_null,
NULL, NULL, NULL,
K_PRIO_PREEMPT(THREAD_TEST_PRIORITY),
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
k_thread_join(tid, K_FOREVER);
}
#else
void test_sem_count_get_null(void)
{
/* For those platform not support userspace, we skip it. */
ztest_test_skip();
}
#endif
/* ztest main entry*/
void test_main(void)
{
#ifdef CONFIG_USERSPACE
k_thread_access_grant(k_current_get(),
&simple_sem, &multiple_thread_sem, &low_prio_sem,
&mid_prio_sem, &high_prio_sem, &ksema, &sema,
&high_prio_long_sem, &stack_1, &stack_2,
&stack_3, &stack_4, &timeout_info_pipe,
&sem_tid_1, &sem_tid_2, &sem_tid_3, &sem_tid_4,
&tstack, &tdata, &mut_sem);
#endif
ztest_test_suite(test_semaphore,
ztest_user_unit_test(test_k_sem_define),
ztest_user_unit_test(test_k_sem_init),
ztest_user_unit_test(test_sem_thread2thread),
ztest_unit_test(test_sem_thread2isr),
ztest_user_unit_test(test_sem_reset),
ztest_user_unit_test(test_sem_reset_waiting),
ztest_user_unit_test(test_sem_count_get),
ztest_unit_test(test_sem_give_from_isr),
ztest_user_unit_test(test_sem_give_from_thread),
ztest_user_unit_test(test_sem_take_no_wait),
ztest_user_unit_test(test_sem_take_no_wait_fails),
ztest_user_unit_test(test_sem_take_timeout_fails),
ztest_user_unit_test(test_sem_take_timeout),
ztest_user_unit_test(test_sem_take_timeout_forever),
ztest_unit_test(test_sem_take_timeout_isr),
ztest_user_unit_test(test_sem_take_multiple),
ztest_unit_test(test_sem_give_take_from_isr),
ztest_user_unit_test(test_k_sem_correct_count_limit),
ztest_unit_test(test_sem_multiple_threads_wait),
ztest_unit_test(test_sem_measure_timeouts),
ztest_unit_test(test_sem_measure_timeout_from_thread),
ztest_1cpu_unit_test(test_sem_multiple_take_and_timeouts),
ztest_unit_test(test_sem_multi_take_timeout_diff_sem),
ztest_user_unit_test(test_sem_give_null),
ztest_user_unit_test(test_sem_init_null),
ztest_user_unit_test(test_sem_take_null),
ztest_user_unit_test(test_sem_reset_null),
ztest_user_unit_test(test_sem_count_get_null),
ztest_1cpu_unit_test(test_sem_queue_mutual_exclusion));
ztest_run_test_suite(test_semaphore);
}