/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include "lifo_usage.h"
#include <kernel.h>
#define STACK_SIZE (1024 + CONFIG_TEST_EXTRA_STACKSIZE)
#define LIST_LEN 2
struct k_lifo lifo, plifo;
static ldata_t data[LIST_LEN];
struct k_lifo timeout_order_lifo;
static struct k_thread tdata, tdata1;
static K_THREAD_STACK_DEFINE(tstack, STACK_SIZE);
static K_THREAD_STACK_DEFINE(tstack1, STACK_SIZE);
static struct k_sem start_sema, wait_sema;
void test_thread_pend_and_timeout(void *p1, void *p2, void *p3);
struct scratch_lifo_packet {
void *link_in_lifo;
void *data_if_needed;
};
struct reply_packet {
void *link_in_lifo;
int32_t reply;
};
struct timeout_order_data {
void *link_in_lifo;
struct k_lifo *klifo;
k_ticks_t timeout;
int32_t timeout_order;
int32_t q_order;
};
static struct k_lifo lifo_timeout[2];
struct timeout_order_data timeout_order_data[] = {
{0, &lifo_timeout[0], 20, 2, 0},
{0, &lifo_timeout[0], 40, 4, 1},
{0, &lifo_timeout[0], 0, 0, 2},
{0, &lifo_timeout[0], 10, 1, 3},
{0, &lifo_timeout[0], 30, 3, 4},
};
struct timeout_order_data timeout_order_data_mult_lifo[] = {
{0, &lifo_timeout[1], 0, 0, 0},
{0, &lifo_timeout[0], 30, 3, 1},
{0, &lifo_timeout[0], 50, 5, 2},
{0, &lifo_timeout[1], 80, 8, 3},
{0, &lifo_timeout[1], 70, 7, 4},
{0, &lifo_timeout[0], 10, 1, 5},
{0, &lifo_timeout[0], 60, 6, 6},
{0, &lifo_timeout[0], 20, 2, 7},
{0, &lifo_timeout[1], 40, 4, 8},
};
#define NUM_SCRATCH_LIFO_PACKETS 20
#define TIMEOUT_ORDER_NUM_THREADS ARRAY_SIZE(timeout_order_data_mult_lifo)
#define TSTACK_SIZE (1024 + CONFIG_TEST_EXTRA_STACKSIZE)
#define LIFO_THREAD_PRIO -5
struct scratch_lifo_packet scratch_lifo_packets[NUM_SCRATCH_LIFO_PACKETS];
struct k_lifo scratch_lifo_packets_lifo;
static k_tid_t tid[TIMEOUT_ORDER_NUM_THREADS];
static K_THREAD_STACK_ARRAY_DEFINE(ttstack,
TIMEOUT_ORDER_NUM_THREADS, TSTACK_SIZE);
static struct k_thread ttdata[TIMEOUT_ORDER_NUM_THREADS];
static void *get_scratch_packet(void)
{
void *packet = k_lifo_get(&scratch_lifo_packets_lifo, K_NO_WAIT);
zassert_true(packet != NULL, NULL);
return packet;
}
static void put_scratch_packet(void *packet)
{
k_lifo_put(&scratch_lifo_packets_lifo, packet);
}
static void thread_entry_nowait(void *p1, void *p2, void *p3)
{
void *ret;
ret = k_lifo_get((struct k_lifo *)p1, K_FOREVER);
/* data pushed at last should be read first */
zassert_equal(ret, (void *)&data[1], NULL);
ret = k_lifo_get((struct k_lifo *)p1, K_FOREVER);
zassert_equal(ret, (void *)&data[0], NULL);
k_sem_give(&start_sema);
}
static bool is_timeout_in_range(uint32_t start_time, uint32_t timeout)
{
uint32_t stop_time, diff;
stop_time = k_cycle_get_32();
diff = (uint32_t)k_cyc_to_ns_floor64(stop_time -
start_time) / NSEC_PER_USEC;
diff = diff / USEC_PER_MSEC;
return timeout <= diff;
}
static int test_multiple_threads_pending(struct timeout_order_data *test_data,
int test_data_size)
{
int ii;
for (ii = 0; ii < test_data_size; ii++) {
tid[ii] = k_thread_create(&ttdata[ii], ttstack[ii], TSTACK_SIZE,
test_thread_pend_and_timeout,
&test_data[ii], NULL, NULL,
LIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
}
for (ii = 0; ii < test_data_size; ii++) {
struct timeout_order_data *data =
k_lifo_get(&timeout_order_lifo, K_FOREVER);
if (data->timeout_order == ii) {
TC_PRINT(" thread (q order: %d, t/o: %d, lifo %p)\n",
data->q_order, (int) data->timeout,
data->klifo);
} else {
zassert_equal(data->timeout_order, ii, " *** thread %d "
"woke up, expected %d\n",
data->timeout_order, ii);
return TC_FAIL;
}
}
return TC_PASS;
}
static void thread_entry_wait(void *p1, void *p2, void *p3)
{
k_lifo_put((struct k_lifo *)p1, (void *)&data[0]);
k_lifo_put((struct k_lifo *)p1, (void *)&data[1]);
k_sem_give(&wait_sema);
}
/**
* @addtogroup kernel_lifo_tests
* @{
*/
/**
* @brief try getting data on lifo with special timeout value,
* return result in lifo
*
*
* @see k_lifo_put()
*/
static void test_thread_timeout_reply_values(void *p1, void *p2, void *p3)
{
struct reply_packet *reply_packet = (struct reply_packet *)p1;
reply_packet->reply =
!!k_lifo_get(&lifo_timeout[0], K_NO_WAIT);
k_lifo_put(&timeout_order_lifo, reply_packet);
}
/**
* @see k_lifo_put()
*/
static void test_thread_timeout_reply_values_wfe(void *p1, void *p2, void *p3)
{
struct reply_packet *reply_packet = (struct reply_packet *)p1;
reply_packet->reply =
!!k_lifo_get(&lifo_timeout[0], K_FOREVER);
k_lifo_put(&timeout_order_lifo, reply_packet);
}
/**
* @brief A thread sleeps then puts data on the lifo
*
* @see k_lifo_put()
*/
static void test_thread_put_timeout(void *p1, void *p2, void *p3)
{
uint32_t timeout = *((uint32_t *)p2);
k_msleep(timeout);
k_lifo_put((struct k_lifo *)p1, get_scratch_packet());
}
/**
* @brief Test last in, first out queue using LIFO
* @see k_sem_init(), k_lifo_put(), k_lifo_get()
*/
static void test_lifo_nowait(void)
{
k_lifo_init(&lifo);
k_sem_init(&start_sema, 0, 1);
/* put some data on lifo */
k_lifo_put(&lifo, (void *)&data[0]);
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
thread_entry_nowait, &lifo, NULL, NULL,
K_PRIO_PREEMPT(0), 0, K_NO_WAIT);
k_lifo_put(&lifo, (void *)&data[1]);
/* Allow another thread to read lifo */
k_sem_take(&start_sema, K_FOREVER);
k_thread_abort(tid);
}
/**
* @brief Test pending reader in LIFO
* @see k_lifo_init(), k_lifo_get(), k_lifo_put()
*/
static void test_lifo_wait(void)
{
int *ret;
k_lifo_init(&plifo);
k_sem_init(&wait_sema, 0, 1);
k_tid_t tid = k_thread_create(&tdata1, tstack1, STACK_SIZE,
thread_entry_wait, &plifo, NULL, NULL,
K_PRIO_PREEMPT(0), 0, K_NO_WAIT);
ret = k_lifo_get(&plifo, K_FOREVER);
zassert_equal(ret, (void *)&data[0], NULL);
k_sem_take(&wait_sema, K_FOREVER);
ret = k_lifo_get(&plifo, K_FOREVER);
zassert_equal(ret, (void *)&data[1], NULL);
k_thread_abort(tid);
}
/**
* @brief Test reading empty LIFO
* @see k_lifo_get()
*/
static void test_timeout_empty_lifo(void)
{
void *packet;
uint32_t start_time, timeout;
timeout = 10U;
start_time = k_cycle_get_32();
packet = k_lifo_get(&lifo_timeout[0], K_MSEC(timeout));
zassert_equal(packet, NULL, NULL);
zassert_true(is_timeout_in_range(start_time, timeout), NULL);
/* Test empty lifo with timeout of K_NO_WAIT */
packet = k_lifo_get(&lifo_timeout[0], K_NO_WAIT);
zassert_equal(packet, NULL, NULL);
}
/**
* @brief Test read and write operation in LIFO with timeout
* @see k_lifo_put(), k_lifo_get()
*/
static void test_timeout_non_empty_lifo(void)
{
void *packet, *scratch_packet;
/* Test k_lifo_get with K_NO_WAIT */
scratch_packet = get_scratch_packet();
k_lifo_put(&lifo_timeout[0], scratch_packet);
packet = k_lifo_get(&lifo_timeout[0], K_NO_WAIT);
zassert_true(packet != NULL, NULL);
put_scratch_packet(scratch_packet);
/* Test k_lifo_get with K_FOREVER */
scratch_packet = get_scratch_packet();
k_lifo_put(&lifo_timeout[0], scratch_packet);
packet = k_lifo_get(&lifo_timeout[0], K_FOREVER);
zassert_true(packet != NULL, NULL);
}
/**
* @brief Test LIFO with timeout
* @see k_lifo_put(), k_lifo_get()
*/
static void test_timeout_lifo_thread(void)
{
void *packet, *scratch_packet;
struct reply_packet reply_packet;
uint32_t start_time, timeout;
/*
* Test lifo with some timeout and child thread that puts
* data on the lifo on time
*/
timeout = 10U;
start_time = k_cycle_get_32();
tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
test_thread_put_timeout, &lifo_timeout[0],
&timeout, NULL,
LIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
packet = k_lifo_get(&lifo_timeout[0], K_MSEC(timeout + 10));
zassert_true(packet != NULL, NULL);
zassert_true(is_timeout_in_range(start_time, timeout), NULL);
put_scratch_packet(packet);
/*
* Test k_lifo_get with timeout of K_NO_WAIT and the lifo
* should be filled be filled by the child thread based on
* the data availability on another lifo. In this test child
* thread does not find data on lifo.
*/
tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
test_thread_timeout_reply_values,
&reply_packet, NULL, NULL,
LIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
k_yield();
packet = k_lifo_get(&timeout_order_lifo, K_NO_WAIT);
zassert_true(packet != NULL, NULL);
zassert_false(reply_packet.reply, NULL);
/*
* Test k_lifo_get with timeout of K_NO_WAIT and the lifo
* should be filled be filled by the child thread based on
* the data availability on another lifo. In this test child
* thread does find data on lifo.
*/
scratch_packet = get_scratch_packet();
k_lifo_put(&lifo_timeout[0], scratch_packet);
tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
test_thread_timeout_reply_values,
&reply_packet, NULL, NULL,
LIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
k_yield();
packet = k_lifo_get(&timeout_order_lifo, K_NO_WAIT);
zassert_true(packet != NULL, NULL);
zassert_true(reply_packet.reply, NULL);
put_scratch_packet(scratch_packet);
/*
* Test k_lifo_get with timeout of K_FOREVER and the lifo
* should be filled be filled by the child thread based on
* the data availability on another lifo. In this test child
* thread does find data on lifo.
*/
scratch_packet = get_scratch_packet();
k_lifo_put(&lifo_timeout[0], scratch_packet);
tid[0] = k_thread_create(&ttdata[0], ttstack[0], TSTACK_SIZE,
test_thread_timeout_reply_values_wfe,
&reply_packet, NULL, NULL,
LIFO_THREAD_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
packet = k_lifo_get(&timeout_order_lifo, K_FOREVER);
zassert_true(packet != NULL, NULL);
zassert_true(reply_packet.reply, NULL);
put_scratch_packet(scratch_packet);
}
/**
* @brief a thread pends on a lifo then times out
* @see k_lifo_put(), k_lifo_get()
*/
void test_thread_pend_and_timeout(void *p1, void *p2, void *p3)
{
struct timeout_order_data *d = (struct timeout_order_data *)p1;
uint32_t start_time;
void *packet;
start_time = k_cycle_get_32();
packet = k_lifo_get(d->klifo, K_MSEC(d->timeout));
zassert_true(packet == NULL, NULL);
zassert_true(is_timeout_in_range(start_time, d->timeout), NULL);
k_lifo_put(&timeout_order_lifo, d);
}
/**
* @brief Test multiple pending readers in LIFO
* @details test multiple threads pending on the same lifo
* with different timeouts
* @see k_lifo_get()
*/
static void test_timeout_threads_pend_on_lifo(void)
{
int32_t rv, test_data_size;
/*
* Test multiple threads pending on the same
* lifo with different timeouts
*/
test_data_size = ARRAY_SIZE(timeout_order_data);
rv = test_multiple_threads_pending(timeout_order_data, test_data_size);
zassert_equal(rv, TC_PASS, NULL);
}
/**
* @brief Test LIFO initialization with various parameters
* @see k_lifo_init(), k_lifo_put()
*/
static void test_para_init(void)
{
intptr_t ii;
/* Init kernel objects*/
k_lifo_init(&lifo_timeout[0]);
k_lifo_init(&lifo_timeout[0]);
k_lifo_init(&timeout_order_lifo);
k_lifo_init(&scratch_lifo_packets_lifo);
/* Fill Scratch LIFO*/
for (ii = 0; ii < NUM_SCRATCH_LIFO_PACKETS; ii++) {
scratch_lifo_packets[ii].data_if_needed = (void *)ii;
k_lifo_put(&scratch_lifo_packets_lifo,
(void *)&scratch_lifo_packets[ii]);
}
for (int i = 0; i < LIST_LEN; i++) {
data[i].data = i + 1;
}
}
/**
* @}
*/
/** test case main entry */
void test_main(void)
{
test_para_init();
ztest_test_suite(test_lifo_usage,
ztest_unit_test(test_lifo_nowait),
ztest_1cpu_unit_test(test_lifo_wait),
ztest_1cpu_unit_test(test_timeout_empty_lifo),
ztest_unit_test(test_timeout_non_empty_lifo),
ztest_1cpu_unit_test(test_timeout_lifo_thread),
ztest_1cpu_unit_test(test_timeout_threads_pend_on_lifo));
ztest_run_test_suite(test_lifo_usage);
}