blob: f2f5736ea10c454eec2c4a18f0d8cec6afcdd427 [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <irq_offload.h>
/* Macro declarations */
#define SEM_INIT_VAL (0U)
#define SEM_MAX_VAL (10U)
#define sem_give_from_isr(sema) irq_offload(isr_sem_give, sema)
#define sem_take_from_isr(sema) irq_offload(isr_sem_take, sema)
#define SEM_TIMEOUT (K_MSEC(100))
#define STACK_SIZE (1024 + CONFIG_TEST_EXTRA_STACKSIZE)
#define TOTAL_THREADS_WAITING (5)
struct timeout_info {
u32_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_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_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, sem_tid_1, sem_tid_2;
struct k_thread multiple_tid[TOTAL_THREADS_WAITING];
/******************************************************************************/
/* Helper functions */
void isr_sem_give(void *semaphore)
{
k_sem_give((struct k_sem *)semaphore);
}
void isr_sem_take(void *semaphore)
{
k_sem_take((struct k_sem *)semaphore, K_NO_WAIT);
}
void sem_give_task(void *p1, void *p2, void *p3)
{
k_sem_give(&simple_sem);
}
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)
{
s32_t ret_value;
ret_value = k_sem_take(&low_prio_sem, K_FOREVER);
zassert_true(ret_value == 0,
"k_sem_take failed when its shouldn't have\n");
ret_value = k_sem_take(&multiple_thread_sem, K_FOREVER);
zassert_true(ret_value == 0,
"k_sem_take failed when its shouldn't have\n");
k_sem_give(&low_prio_sem);
}
void sem_take_multiple_mid_prio_helper(void *p1, void *p2, void *p3)
{
s32_t ret_value;
ret_value = k_sem_take(&mid_prio_sem, K_FOREVER);
zassert_true(ret_value == 0,
"k_sem_take failed when its shouldn't have\n");
ret_value = k_sem_take(&multiple_thread_sem, K_FOREVER);
zassert_true(ret_value == 0,
"k_sem_take failed when its shouldn't have\n");
k_sem_give(&mid_prio_sem);
}
void sem_take_multiple_high_prio_helper(void *p1, void *p2, void *p3)
{
s32_t ret_value;
ret_value = k_sem_take(&high_prio_sem, K_FOREVER);
zassert_true(ret_value == 0,
"k_sem_take failed when its shouldn't have\n");
ret_value = k_sem_take(&multiple_thread_sem, K_FOREVER);
zassert_true(ret_value == 0,
"k_sem_take failed when its shouldn't have\n");
k_sem_give(&high_prio_sem);
}
/**
* @ingroup kernel_semaphore_tests
* @{
*/
/**
* @brief Test semaphore count when given by an ISR
* @see k_sem_give()
*/
void test_simple_sem_from_isr(void)
{
u32_t signal_count;
/*
* Signal the semaphore several times from an ISR. After each signal,
* check the signal count.
*/
for (int i = 0; i < 5; i++) {
sem_give_from_isr(&simple_sem);
signal_count = k_sem_count_get(&simple_sem);
zassert_true(signal_count == (i + 1),
"signal count missmatch Expected %d, got %d\n",
(i + 1), signal_count);
}
}
/**
* @brief Test semaphore count when given by thread
* @see k_sem_give()
*/
void test_simple_sem_from_task(void)
{
u32_t signal_count;
/*
* Signal the semaphore several times from a task. After each signal,
* check the signal count.
*/
k_sem_reset(&simple_sem);
for (int i = 0; i < 5; i++) {
k_sem_give(&simple_sem);
signal_count = k_sem_count_get(&simple_sem);
zassert_true(signal_count == (i + 1),
"signal count missmatch Expected %d, got %d\n",
(i + 1), signal_count);
}
}
/**
* @brief Test if k_sem_take() decreases semaphore count
* @see k_sem_take()
*/
void test_sem_take_no_wait(void)
{
u32_t signal_count;
s32_t ret_value;
/*
* Test the semaphore without wait. Check the signal count after each
* attempt (it should be decrementing by 1 each time).
*/
for (int i = 4; i >= 0; i--) {
ret_value = k_sem_take(&simple_sem, K_NO_WAIT);
zassert_true(ret_value == 0,
"unable to do k_sem_take which returned %d\n",
ret_value);
signal_count = k_sem_count_get(&simple_sem);
zassert_true(signal_count == i,
"signal count missmatch Expected %d, got %d\n",
i, signal_count);
}
}
/**
* @brief Test k_sem_take() when there is no semaphore to take
* @see k_sem_take()
*/
void test_sem_take_no_wait_fails(void)
{
u32_t signal_count;
s32_t ret_value;
/*
* 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 = 4; i >= 0; i--) {
ret_value = k_sem_take(&simple_sem, K_NO_WAIT);
zassert_true(ret_value == -EBUSY,
"k_sem_take returned when not possible");
signal_count = k_sem_count_get(&simple_sem);
zassert_true(signal_count == 0U,
"signal count missmatch Expected 0, got %d\n",
signal_count);
}
}
/**
* @brief Test k_sem_take() with timeout expiry
* @see k_sem_take()
*/
void test_sem_take_timeout_fails(void)
{
s32_t ret_value;
/*
* Test the semaphore with timeout without a k_sem_give.
*/
k_sem_reset(&simple_sem);
for (int i = 4; i >= 0; i--) {
ret_value = k_sem_take(&simple_sem, SEM_TIMEOUT);
zassert_true(ret_value == -EAGAIN,
"k_sem_take succeeded when its not possible");
}
}
/**
* @brief Test k_sem_take() with timeout
* @see k_sem_take()
*/
void test_sem_take_timeout(void)
{
s32_t ret_value;
/*
* Signal the semaphore upon which the another thread is waiting. The
* alternate task (which is at a lower priority) will cause simple_sem
* to be signalled, thus waking this task.
*/
k_thread_create(&sem_tid, stack_1, STACK_SIZE,
sem_give_task, NULL, NULL, NULL,
K_PRIO_PREEMPT(0), K_USER | K_INHERIT_PERMS,
K_NO_WAIT);
k_sem_reset(&simple_sem);
ret_value = k_sem_take(&simple_sem, SEM_TIMEOUT);
zassert_true(ret_value == 0,
"k_sem_take failed when its shouldn't have");
k_thread_abort(&sem_tid);
}
/**
* @brief Test k_sem_take() with forever timeout
* @see k_sem_take()
*/
void test_sem_take_timeout_forever(void)
{
s32_t ret_value;
/*
* Signal the semaphore upon which the another thread is waiting. The
* alternate task (which is at a lower priority) will cause simple_sem
* to be signalled, thus waking this task.
*/
k_thread_create(&sem_tid, 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);
ret_value = k_sem_take(&simple_sem, K_FOREVER);
zassert_true(ret_value == 0,
"k_sem_take failed when its shouldn't have");
k_thread_abort(&sem_tid);
}
/**
* @brief Test k_sem_take() with timeout in ISR context
* @see k_sem_take()
*/
void test_sem_take_timeout_isr(void)
{
s32_t ret_value;
/*
* Signal the semaphore upon which the another thread is waiting. The
* alternate task (which is at a lower priority) will cause simple_sem
* to be signalled, thus waking this task.
*/
k_thread_create(&sem_tid, 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);
ret_value = k_sem_take(&simple_sem, SEM_TIMEOUT);
zassert_true(ret_value == 0,
"k_sem_take failed when its shouldn't have");
k_thread_abort(&sem_tid);
}
/**
* @brief Test multiple semaphore take
* @see k_sem_take()
*/
void test_sem_take_multiple(void)
{
u32_t signal_count;
/*
* Signal the semaphore upon which the another thread is waiting. The
* alternate task (which is at a lower priority) will cause simple_sem
* to be signalled, thus waking this task.
*/
k_thread_create(&sem_tid, 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_1, 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_2, stack_3, 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 3 threads to complete */
k_sleep(K_MSEC(20));
/* Let these threads proceed to take the multiple_sem */
k_sem_give(&high_prio_sem);
k_sem_give(&mid_prio_sem);
k_sem_give(&low_prio_sem);
k_sleep(K_MSEC(200));
/* enable the higher priority thread to run. */
k_sem_give(&multiple_thread_sem);
k_sleep(K_MSEC(200));
/* check which threads completed. */
signal_count = k_sem_count_get(&high_prio_sem);
zassert_true(signal_count == 1U,
"Higher priority threads didn't execute");
signal_count = k_sem_count_get(&mid_prio_sem);
zassert_true(signal_count == 0U,
"Medium priority threads shouldn't have executed");
signal_count = k_sem_count_get(&low_prio_sem);
zassert_true(signal_count == 0U,
"low priority threads shouldn't have executed");
/* enable the Medium priority thread to run. */
k_sem_give(&multiple_thread_sem);
k_sleep(K_MSEC(200));
/* check which threads completed. */
signal_count = k_sem_count_get(&high_prio_sem);
zassert_true(signal_count == 1U,
"Higher priority thread executed again");
signal_count = k_sem_count_get(&mid_prio_sem);
zassert_true(signal_count == 1U,
"Medium priority thread didn't get executed");
signal_count = k_sem_count_get(&low_prio_sem);
zassert_true(signal_count == 0U,
"low priority thread shouldn't have executed");
/* enable the low priority thread to run. */
k_sem_give(&multiple_thread_sem);
k_sleep(K_MSEC(200));
/* check which threads completed. */
signal_count = k_sem_count_get(&high_prio_sem);
zassert_true(signal_count == 1U,
"Higher priority thread executed again");
signal_count = k_sem_count_get(&mid_prio_sem);
zassert_true(signal_count == 1U,
"Medium priority thread executed again");
signal_count = k_sem_count_get(&low_prio_sem);
zassert_true(signal_count == 1U,
"low priority thread didn't get executed");
}
/**
* @brief Test semaphore give and take and its count from ISR
* @see k_sem_give()
*/
void test_sem_give_take_from_isr(void)
{
u32_t signal_count;
k_sem_reset(&simple_sem);
/* 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);
signal_count = k_sem_count_get(&simple_sem);
zassert_true(signal_count == i + 1,
"signal count missmatch Expected %d, got %d\n",
i + 1, signal_count);
}
/* 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);
signal_count = k_sem_count_get(&simple_sem);
zassert_true(signal_count == (i - 1),
"signal count missmatch Expected %d, got %d\n",
(i - 1), signal_count);
}
}
/**
* @}
*/
void test_sem_multiple_threads_wait_helper(void *p1, void *p2, void *p3)
{
/* get blocked until the test thread gives the semaphore */
k_sem_take(&multiple_thread_sem, K_FOREVER);
/* 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)
{
u32_t signal_count;
s32_t ret_value;
u32_t repeat_count = 0U;
k_sem_reset(&simple_sem);
k_sem_reset(&multiple_thread_sem);
do {
for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
k_thread_create(&multiple_tid[i],
multiple_stack[i], STACK_SIZE,
test_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++) {
ret_value = k_sem_take(&simple_sem, K_FOREVER);
zassert_true(ret_value == 0,
"Some of the threads didn't get multiple_thread_sem\n"
);
}
signal_count = k_sem_count_get(&simple_sem);
zassert_true(signal_count == 0U,
"signal count missmatch Expected 0, got %d\n",
signal_count);
signal_count = k_sem_count_get(&multiple_thread_sem);
zassert_true(signal_count == 0U,
"signal count missmatch Expected 0, got %d\n",
signal_count);
/* Verify a wait q that has been emptied / reset
* correctly by running again.
*/
repeat_count++;
} while (repeat_count < 2);
}
/**
* @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)
{
s32_t ret_value;
u32_t start_ticks, end_ticks;
k_sem_reset(&simple_sem);
/* With timeout of 1 sec */
start_ticks = k_uptime_get();
ret_value = k_sem_take(&simple_sem, K_SECONDS(1));
end_ticks = k_uptime_get();
zassert_true(ret_value == -EAGAIN,
"k_sem_take failed when its shouldn't have");
zassert_true((end_ticks - start_ticks >= K_SECONDS(1)),
"time missmatch expected %d ,got %d\n",
K_SECONDS(1), end_ticks - start_ticks);
/* With 0 as the timeout */
start_ticks = k_uptime_get();
ret_value = k_sem_take(&simple_sem, 0);
end_ticks = k_uptime_get();
zassert_true(ret_value == -EBUSY,
"k_sem_take failed when its shouldn't have");
zassert_true((end_ticks - start_ticks < 1),
"time missmatch expected %d ,got %d\n",
1, end_ticks - start_ticks);
}
void test_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)
{
s32_t ret_value;
u32_t start_ticks, end_ticks;
k_sem_reset(&simple_sem);
k_sem_reset(&multiple_thread_sem);
/* Give a semaphore from a thread and calcualte the time taken.*/
k_thread_create(&sem_tid, stack_1, STACK_SIZE,
test_sem_measure_timeout_from_thread_helper,
NULL, NULL, NULL,
K_PRIO_PREEMPT(3), 0, K_NO_WAIT);
/* first sync the 2 threads */
k_sem_take(&simple_sem, K_FOREVER);
/* With timeout of 1 sec */
start_ticks = k_uptime_get();
ret_value = k_sem_take(&multiple_thread_sem, K_SECONDS(1));
end_ticks = k_uptime_get();
zassert_true(ret_value == 0,
"k_sem_take failed when its shouldn't have");
zassert_true((end_ticks - start_ticks <= K_SECONDS(1)),
"time missmatch. expected less than%d ,got %d\n",
K_SECONDS(1), end_ticks - start_ticks);
}
void test_sem_multiple_take_and_timeouts_helper(void *timeout,
void *p2,
void *p3)
{
u32_t start_ticks, end_ticks;
size_t bytes_written;
start_ticks = k_uptime_get();
k_sem_take(&simple_sem, (int)timeout);
end_ticks = k_uptime_get();
zassert_true((end_ticks - start_ticks >= (int)timeout),
"time missmatch. expected less than%d ,got %d\n",
timeout, end_ticks - start_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)
{
u32_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,
test_sem_multiple_take_and_timeouts_helper,
(void *)K_SECONDS(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_true(timeout == K_SECONDS(i + 1),
"timeout didn't occur properly");
}
/* cleanup */
for (int i = 0; i < TOTAL_THREADS_WAITING; i++) {
k_thread_abort(&multiple_tid[i]);
}
}
void test_sem_multi_take_timeout_diff_sem_helper(void *timeout,
void *sema,
void *p3)
{
u32_t start_ticks, end_ticks;
s32_t ret_value;
size_t bytes_written;
struct timeout_info info = {
.timeout = (u32_t) timeout,
.sema = (struct k_sem *)sema
};
start_ticks = k_uptime_get();
ret_value = k_sem_take((struct k_sem *)sema, (int)timeout);
end_ticks = k_uptime_get();
zassert_true((end_ticks - start_ticks >= (int)timeout),
"time missmatch. expected less than%d ,got %d\n",
timeout, end_ticks - start_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)
{
size_t bytes_read;
struct timeout_info seq_info[] = {
{ K_SECONDS(2), &simple_sem },
{ K_SECONDS(1), &multiple_thread_sem },
{ K_SECONDS(3), &simple_sem },
{ K_SECONDS(5), &multiple_thread_sem },
{ K_SECONDS(4), &simple_sem },
};
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,
test_sem_multi_take_timeout_diff_sem_helper,
(void *)seq_info[i].timeout,
(void *)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 == K_SECONDS(i + 1),
"timeout didn't occur properly");
}
}
/* ztest main entry*/
void test_main(void)
{
k_thread_access_grant(k_current_get(),
&simple_sem, &multiple_thread_sem,
&low_prio_sem, &mid_prio_sem, &high_prio_sem,
&stack_1, &stack_2, &stack_3, &timeout_info_pipe,
&sem_tid, &sem_tid_1, &sem_tid_2);
ztest_test_suite(test_semaphore,
ztest_unit_test(test_simple_sem_from_isr),
ztest_user_unit_test(test_simple_sem_from_task),
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_unit_test(test_sem_multiple_threads_wait),
ztest_unit_test(test_sem_measure_timeouts),
ztest_unit_test(test_sem_measure_timeout_from_thread),
ztest_unit_test(test_sem_multiple_take_and_timeouts),
ztest_unit_test(test_sem_multi_take_timeout_diff_sem));
ztest_run_test_suite(test_semaphore);
}
/******************************************************************************/