Although many platforms may have an unmapped address in common, this address will not be the same for all platforms. This commit makes such an address configurable to get better coverage. Signed-off-by: Peter Mitsis <peter.mitsis@intel.com>
666 lines
18 KiB
C
666 lines
18 KiB
C
/*
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
|
|
/**
|
|
* @brief Thread Tests
|
|
* @defgroup kernel_thread_tests Threads
|
|
* @ingroup all_tests
|
|
* @{
|
|
* @}
|
|
*/
|
|
|
|
#include <zephyr/ztest.h>
|
|
#include <zephyr/kernel_structs.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <kernel_internal.h>
|
|
#include <string.h>
|
|
|
|
/* internal kernel APIs */
|
|
#include <ksched.h>
|
|
#include <kthread.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(test);
|
|
|
|
struct k_thread tdata;
|
|
#define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
|
|
K_THREAD_STACK_DEFINE(tstack, STACK_SIZE);
|
|
size_t tstack_size = K_THREAD_STACK_SIZEOF(tstack);
|
|
|
|
/*local variables*/
|
|
static K_THREAD_STACK_DEFINE(tstack_custom, STACK_SIZE);
|
|
static K_THREAD_STACK_DEFINE(tstack_name, STACK_SIZE);
|
|
static struct k_thread tdata_custom;
|
|
static struct k_thread tdata_name;
|
|
|
|
static int main_prio;
|
|
static ZTEST_DMEM int tp = 10;
|
|
|
|
/**
|
|
* @ingroup kernel_thread_tests
|
|
* @brief Verify main thread
|
|
*/
|
|
ZTEST(threads_lifecycle, test_systhreads_main)
|
|
{
|
|
zassert_true(main_prio == CONFIG_MAIN_THREAD_PRIORITY, "%d", CONFIG_MAIN_THREAD_PRIORITY);
|
|
}
|
|
|
|
/**
|
|
* @ingroup kernel_thread_tests
|
|
* @brief Verify idle thread
|
|
*/
|
|
ZTEST(threads_lifecycle, test_systhreads_idle)
|
|
{
|
|
k_msleep(100);
|
|
/** TESTPOINT: check working thread priority should */
|
|
zassert_true(k_thread_priority_get(k_current_get()) <
|
|
K_IDLE_PRIO, NULL);
|
|
}
|
|
|
|
static void customdata_entry(void *p1, void *p2, void *p3)
|
|
{
|
|
long data = 1U;
|
|
|
|
zassert_is_null(k_thread_custom_data_get(), NULL);
|
|
while (1) {
|
|
k_thread_custom_data_set((void *)data);
|
|
/* relinquish cpu for a while */
|
|
k_msleep(50);
|
|
/** TESTPOINT: custom data comparison */
|
|
zassert_equal(data, (long)k_thread_custom_data_get());
|
|
data++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ingroup kernel_thread_tests
|
|
* @brief test thread custom data get/set from coop thread
|
|
*
|
|
* @see k_thread_custom_data_get(), k_thread_custom_data_set()
|
|
*/
|
|
ZTEST(threads_lifecycle_1cpu, test_customdata_get_set_coop)
|
|
{
|
|
k_tid_t tid = k_thread_create(&tdata_custom, tstack_custom, STACK_SIZE,
|
|
customdata_entry, NULL, NULL, NULL,
|
|
K_PRIO_COOP(1), 0, K_NO_WAIT);
|
|
|
|
k_msleep(500);
|
|
|
|
/* cleanup environment */
|
|
k_thread_abort(tid);
|
|
}
|
|
|
|
static void thread_name_entry(void *p1, void *p2, void *p3)
|
|
{
|
|
/* Do nothing and exit */
|
|
}
|
|
|
|
/**
|
|
* @ingroup kernel_thread_tests
|
|
* @brief test thread name get/set from supervisor thread
|
|
* @see k_thread_name_get(), k_thread_name_copy(), k_thread_name_set()
|
|
*/
|
|
ZTEST(threads_lifecycle, test_thread_name_get_set)
|
|
{
|
|
int ret;
|
|
const char *thread_name;
|
|
char thread_buf[CONFIG_THREAD_MAX_NAME_LEN];
|
|
|
|
/* Set and get current thread's name */
|
|
ret = k_thread_name_set(NULL, "parent_thread");
|
|
zassert_equal(ret, 0, "k_thread_name_set() failed");
|
|
thread_name = k_thread_name_get(k_current_get());
|
|
zassert_true(thread_name != NULL, "thread name was null");
|
|
ret = strcmp(thread_name, "parent_thread");
|
|
zassert_equal(ret, 0, "parent thread name does not match");
|
|
|
|
/* Set and get child thread's name */
|
|
k_tid_t tid = k_thread_create(&tdata_name, tstack_name, STACK_SIZE,
|
|
thread_name_entry, NULL, NULL, NULL,
|
|
K_PRIO_PREEMPT(1), 0, K_NO_WAIT);
|
|
|
|
ret = k_thread_name_set(tid, "customdata");
|
|
zassert_equal(ret, 0, "k_thread_name_set() failed");
|
|
ret = k_thread_name_copy(tid, thread_buf, sizeof(thread_buf));
|
|
zassert_equal(ret, 0, "couldn't get copied thread name");
|
|
ret = strcmp(thread_buf, "customdata");
|
|
zassert_equal(ret, 0, "child thread name does not match");
|
|
|
|
/* cleanup environment */
|
|
k_thread_abort(tid);
|
|
}
|
|
|
|
#ifdef CONFIG_USERSPACE
|
|
static char unreadable_string[64];
|
|
static char not_my_buffer[CONFIG_THREAD_MAX_NAME_LEN];
|
|
struct k_sem sem;
|
|
#endif /* CONFIG_USERSPACE */
|
|
|
|
/**
|
|
* @ingroup kernel_thread_tests
|
|
* @brief test thread name get/set from user thread
|
|
* @see k_thread_name_copy(), k_thread_name_set()
|
|
*/
|
|
ZTEST_USER(threads_lifecycle, test_thread_name_user_get_set)
|
|
{
|
|
#ifdef CONFIG_USERSPACE
|
|
int ret;
|
|
char thread_name[CONFIG_THREAD_MAX_NAME_LEN];
|
|
char too_small[2];
|
|
|
|
/* Some memory-related error cases for k_thread_name_set() */
|
|
#if !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE)
|
|
/* Non-Secure images cannot normally access memory outside the image
|
|
* flash and ram.
|
|
*/
|
|
ret = k_thread_name_set(NULL, (const char *)CONFIG_THREAD_API_UNMAPPED_ADDRESS);
|
|
zassert_equal(ret, -EFAULT, "accepted nonsense string (%d)", ret);
|
|
#endif
|
|
ret = k_thread_name_set(NULL, unreadable_string);
|
|
zassert_equal(ret, -EFAULT, "accepted unreadable string");
|
|
ret = k_thread_name_set((struct k_thread *)&sem, "some name");
|
|
zassert_equal(ret, -EINVAL, "accepted non-thread object");
|
|
ret = k_thread_name_set(&z_main_thread, "some name");
|
|
zassert_equal(ret, -EINVAL, "no permission on thread object");
|
|
|
|
/* Set and get current thread's name */
|
|
ret = k_thread_name_set(NULL, "parent_thread");
|
|
zassert_equal(ret, 0, "k_thread_name_set() failed");
|
|
ret = k_thread_name_copy(k_current_get(), thread_name,
|
|
sizeof(thread_name));
|
|
zassert_equal(ret, 0, "k_thread_name_copy() failed");
|
|
ret = strcmp(thread_name, "parent_thread");
|
|
zassert_equal(ret, 0, "parent thread name does not match");
|
|
|
|
/* memory-related cases for k_thread_name_get() */
|
|
ret = k_thread_name_copy(k_current_get(), too_small,
|
|
sizeof(too_small));
|
|
zassert_equal(ret, -ENOSPC, "wrote to too-small buffer");
|
|
ret = k_thread_name_copy(k_current_get(), not_my_buffer,
|
|
sizeof(not_my_buffer));
|
|
zassert_equal(ret, -EFAULT, "wrote to buffer without permission");
|
|
ret = k_thread_name_copy((struct k_thread *)&sem, thread_name,
|
|
sizeof(thread_name));
|
|
zassert_equal(ret, -EINVAL, "not a thread object");
|
|
ret = k_thread_name_copy(&z_main_thread, thread_name,
|
|
sizeof(thread_name));
|
|
zassert_equal(ret, 0, "couldn't get main thread name: %s (%d)", thread_name, ret);
|
|
LOG_DBG("Main thread name is '%s'", thread_name);
|
|
|
|
/* Set and get child thread's name */
|
|
k_tid_t tid = k_thread_create(&tdata_name, tstack_name, STACK_SIZE,
|
|
thread_name_entry, NULL, NULL, NULL,
|
|
K_PRIO_PREEMPT(1), K_USER, K_NO_WAIT);
|
|
ret = k_thread_name_set(tid, "customdata");
|
|
zassert_equal(ret, 0, "k_thread_name_set() failed");
|
|
ret = k_thread_name_copy(tid, thread_name, sizeof(thread_name));
|
|
zassert_equal(ret, 0, "couldn't get copied thread name");
|
|
ret = strcmp(thread_name, "customdata");
|
|
zassert_equal(ret, 0, "child thread name does not match");
|
|
|
|
/* cleanup environment */
|
|
k_thread_abort(tid);
|
|
#else
|
|
ztest_test_skip();
|
|
#endif /* CONFIG_USERSPACE */
|
|
}
|
|
|
|
/**
|
|
* @ingroup kernel_thread_tests
|
|
* @brief test thread custom data get/set from preempt thread
|
|
* @see k_thread_custom_data_get(), k_thread_custom_data_set()
|
|
*/
|
|
ZTEST_USER(threads_lifecycle_1cpu, test_customdata_get_set_preempt)
|
|
{
|
|
/** TESTPOINT: custom data of preempt thread */
|
|
k_tid_t tid = k_thread_create(&tdata_custom, tstack_custom, STACK_SIZE,
|
|
customdata_entry, NULL, NULL, NULL,
|
|
K_PRIO_PREEMPT(0), K_USER, K_NO_WAIT);
|
|
|
|
k_msleep(500);
|
|
|
|
/* cleanup environment */
|
|
k_thread_abort(tid);
|
|
}
|
|
|
|
static void umode_entry(void *thread_id, void *p2, void *p3)
|
|
{
|
|
ARG_UNUSED(p2);
|
|
ARG_UNUSED(p3);
|
|
|
|
if (!z_is_thread_essential(_current) &&
|
|
(k_current_get() == (k_tid_t)thread_id)) {
|
|
ztest_test_pass();
|
|
} else {
|
|
zassert_unreachable("User thread is essential or thread"
|
|
" structure is corrupted\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ingroup kernel_thread_tests
|
|
* @brief Test k_thread_user_mode_enter() to cover when userspace
|
|
* is not supported/enabled
|
|
* @see k_thread_user_mode_enter()
|
|
*/
|
|
static void enter_user_mode_entry(void *p1, void *p2, void *p3)
|
|
{
|
|
z_thread_essential_set(_current);
|
|
|
|
zassert_true(z_is_thread_essential(_current), "Thread isn't set"
|
|
" as essential\n");
|
|
|
|
k_thread_user_mode_enter(umode_entry,
|
|
k_current_get(), NULL, NULL);
|
|
}
|
|
|
|
ZTEST_USER(threads_lifecycle, test_user_mode)
|
|
{
|
|
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
|
|
enter_user_mode_entry, NULL, NULL,
|
|
NULL, main_prio, K_INHERIT_PERMS, K_NO_WAIT);
|
|
k_msleep(100);
|
|
k_thread_abort(tid);
|
|
}
|
|
|
|
struct k_thread join_thread;
|
|
K_THREAD_STACK_DEFINE(join_stack, STACK_SIZE);
|
|
|
|
struct k_thread control_thread;
|
|
K_THREAD_STACK_DEFINE(control_stack, STACK_SIZE);
|
|
|
|
enum control_method {
|
|
TIMEOUT,
|
|
NO_WAIT,
|
|
SELF_ABORT,
|
|
OTHER_ABORT,
|
|
OTHER_ABORT_TIMEOUT,
|
|
ALREADY_EXIT,
|
|
ISR_ALREADY_EXIT,
|
|
ISR_RUNNING
|
|
};
|
|
|
|
static void join_entry(void *p1, void *p2, void *p3)
|
|
{
|
|
enum control_method m = (enum control_method)(intptr_t)p1;
|
|
|
|
switch (m) {
|
|
case TIMEOUT:
|
|
case NO_WAIT:
|
|
case OTHER_ABORT:
|
|
case OTHER_ABORT_TIMEOUT:
|
|
case ISR_RUNNING:
|
|
LOG_DBG("join_thread: sleeping forever");
|
|
k_sleep(K_FOREVER);
|
|
break;
|
|
case SELF_ABORT:
|
|
case ALREADY_EXIT:
|
|
case ISR_ALREADY_EXIT:
|
|
LOG_DBG("join_thread: self-exiting");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void control_entry(void *p1, void *p2, void *p3)
|
|
{
|
|
LOG_DBG("control_thread: killing join thread");
|
|
k_thread_abort(&join_thread);
|
|
}
|
|
|
|
static void do_join_from_isr(const void *arg)
|
|
{
|
|
int *ret = (int *)arg;
|
|
|
|
zassert_true(k_is_in_isr());
|
|
LOG_DBG("isr: joining join_thread");
|
|
*ret = k_thread_join(&join_thread, K_NO_WAIT);
|
|
LOG_DBG("isr: k_thread_join() returned with %d", *ret);
|
|
}
|
|
|
|
#define JOIN_TIMEOUT_MS 100
|
|
|
|
static int join_scenario_interval(enum control_method m, int64_t *interval)
|
|
{
|
|
k_timeout_t timeout = K_FOREVER;
|
|
int ret;
|
|
|
|
LOG_DBG("ztest_thread: method %d, create join_thread", m);
|
|
k_thread_create(&join_thread, join_stack, STACK_SIZE, join_entry,
|
|
(void *)m, NULL, NULL, K_PRIO_PREEMPT(1),
|
|
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
|
|
|
|
switch (m) {
|
|
case ALREADY_EXIT:
|
|
case ISR_ALREADY_EXIT:
|
|
/* Let join_thread run first */
|
|
k_msleep(50);
|
|
break;
|
|
case OTHER_ABORT_TIMEOUT:
|
|
timeout = K_MSEC(JOIN_TIMEOUT_MS);
|
|
__fallthrough;
|
|
case OTHER_ABORT:
|
|
LOG_DBG("ztest_thread: create control_thread");
|
|
k_thread_create(&control_thread, control_stack, STACK_SIZE,
|
|
control_entry, NULL, NULL, NULL,
|
|
K_PRIO_PREEMPT(2),
|
|
K_USER | K_INHERIT_PERMS, K_NO_WAIT);
|
|
break;
|
|
case TIMEOUT:
|
|
timeout = K_MSEC(50);
|
|
break;
|
|
case NO_WAIT:
|
|
timeout = K_NO_WAIT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (m == ISR_ALREADY_EXIT || m == ISR_RUNNING) {
|
|
irq_offload(do_join_from_isr, (const void *)&ret);
|
|
} else {
|
|
LOG_DBG("ztest_thread: joining join_thread");
|
|
|
|
if (interval != NULL) {
|
|
*interval = k_uptime_get();
|
|
}
|
|
|
|
ret = k_thread_join(&join_thread, timeout);
|
|
|
|
if (interval != NULL) {
|
|
*interval = k_uptime_get() - *interval;
|
|
}
|
|
|
|
LOG_DBG("ztest_thread: k_thread_join() returned with %d", ret);
|
|
}
|
|
|
|
if (ret != 0) {
|
|
k_thread_abort(&join_thread);
|
|
}
|
|
if (m == OTHER_ABORT || m == OTHER_ABORT_TIMEOUT) {
|
|
k_thread_join(&control_thread, K_FOREVER);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline int join_scenario(enum control_method m)
|
|
{
|
|
return join_scenario_interval(m, NULL);
|
|
}
|
|
|
|
ZTEST_USER(threads_lifecycle, test_thread_join)
|
|
{
|
|
int64_t interval;
|
|
|
|
#ifdef CONFIG_USERSPACE
|
|
/* scenario: thread never started */
|
|
zassert_equal(k_thread_join(&join_thread, K_FOREVER), 0,
|
|
"failed case thread never started");
|
|
#endif
|
|
zassert_equal(join_scenario(TIMEOUT), -EAGAIN, "failed timeout case");
|
|
zassert_equal(join_scenario(NO_WAIT), -EBUSY, "failed no-wait case");
|
|
zassert_equal(join_scenario(SELF_ABORT), 0, "failed self-abort case");
|
|
zassert_equal(join_scenario(OTHER_ABORT), 0, "failed other-abort case");
|
|
|
|
zassert_equal(join_scenario_interval(OTHER_ABORT_TIMEOUT, &interval),
|
|
0, "failed other-abort case with timeout");
|
|
zassert_true(interval < JOIN_TIMEOUT_MS, "join took too long (%lld ms)",
|
|
interval);
|
|
zassert_equal(join_scenario(ALREADY_EXIT), 0,
|
|
"failed already exit case");
|
|
|
|
}
|
|
|
|
ZTEST(threads_lifecycle, test_thread_join_isr)
|
|
{
|
|
zassert_equal(join_scenario(ISR_RUNNING), -EBUSY, "failed isr running");
|
|
zassert_equal(join_scenario(ISR_ALREADY_EXIT), 0, "failed isr exited");
|
|
}
|
|
|
|
struct k_thread deadlock1_thread;
|
|
K_THREAD_STACK_DEFINE(deadlock1_stack, STACK_SIZE);
|
|
|
|
struct k_thread deadlock2_thread;
|
|
K_THREAD_STACK_DEFINE(deadlock2_stack, STACK_SIZE);
|
|
|
|
static void deadlock1_entry(void *p1, void *p2, void *p3)
|
|
{
|
|
int ret;
|
|
|
|
k_msleep(500);
|
|
|
|
ret = k_thread_join(&deadlock2_thread, K_FOREVER);
|
|
zassert_equal(ret, -EDEADLK, "failed mutual join case");
|
|
}
|
|
|
|
static void deadlock2_entry(void *p1, void *p2, void *p3)
|
|
{
|
|
int ret;
|
|
|
|
/* deadlock1_thread is active but currently sleeping */
|
|
ret = k_thread_join(&deadlock1_thread, K_FOREVER);
|
|
|
|
zassert_equal(ret, 0, "couldn't join deadlock2_thread");
|
|
}
|
|
|
|
ZTEST_USER(threads_lifecycle, test_thread_join_deadlock)
|
|
{
|
|
/* Deadlock scenarios */
|
|
zassert_equal(k_thread_join(k_current_get(), K_FOREVER), -EDEADLK,
|
|
"failed self-deadlock case");
|
|
|
|
k_thread_create(&deadlock1_thread, deadlock1_stack, STACK_SIZE,
|
|
deadlock1_entry, NULL, NULL, NULL,
|
|
K_PRIO_PREEMPT(1), K_USER | K_INHERIT_PERMS, K_NO_WAIT);
|
|
k_thread_create(&deadlock2_thread, deadlock2_stack, STACK_SIZE,
|
|
deadlock2_entry, NULL, NULL, NULL,
|
|
K_PRIO_PREEMPT(1), K_USER | K_INHERIT_PERMS, K_NO_WAIT);
|
|
|
|
zassert_equal(k_thread_join(&deadlock1_thread, K_FOREVER), 0,
|
|
"couldn't join deadlock1_thread");
|
|
zassert_equal(k_thread_join(&deadlock2_thread, K_FOREVER), 0,
|
|
"couldn't join deadlock2_thread");
|
|
}
|
|
|
|
#define WAIT_TO_START_MS 100
|
|
/*
|
|
* entry for a delayed thread, do nothing. After the thread is created,
|
|
* just check how many ticks expires and how many ticks remain before
|
|
* the thread start
|
|
*/
|
|
static void user_start_thread(void *p1, void *p2, void *p3)
|
|
{
|
|
/* do nothing */
|
|
}
|
|
|
|
ZTEST_USER(threads_lifecycle, test_thread_timeout_remaining_expires)
|
|
{
|
|
k_ticks_t r, e, r1, ticks, expected_expires_ticks;
|
|
|
|
ticks = k_ms_to_ticks_ceil32(WAIT_TO_START_MS);
|
|
expected_expires_ticks = k_uptime_ticks() + ticks;
|
|
|
|
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
|
|
user_start_thread, k_current_get(), NULL,
|
|
NULL, 0, K_USER,
|
|
K_MSEC(WAIT_TO_START_MS));
|
|
|
|
k_msleep(10);
|
|
e = k_thread_timeout_expires_ticks(tid);
|
|
LOG_DBG("thread_expires_ticks: %d, expect: %d", (int)e,
|
|
(int)expected_expires_ticks);
|
|
zassert_true(e >= expected_expires_ticks);
|
|
|
|
k_msleep(10);
|
|
r = k_thread_timeout_remaining_ticks(tid);
|
|
zassert_true(r < ticks);
|
|
r1 = r;
|
|
|
|
k_msleep(10);
|
|
r = k_thread_timeout_remaining_ticks(tid);
|
|
zassert_true(r < r1);
|
|
|
|
k_thread_abort(tid);
|
|
}
|
|
|
|
static void foreach_callback(const struct k_thread *thread, void *user_data)
|
|
{
|
|
|
|
k_thread_runtime_stats_t stats;
|
|
int ret;
|
|
|
|
if (z_is_idle_thread_object((k_tid_t)thread)) {
|
|
return;
|
|
}
|
|
|
|
/* Check NULL parameters */
|
|
ret = k_thread_runtime_stats_get(NULL, &stats);
|
|
zassert_true(ret == -EINVAL);
|
|
ret = k_thread_runtime_stats_get((k_tid_t)thread, NULL);
|
|
zassert_true(ret == -EINVAL);
|
|
|
|
k_thread_runtime_stats_get((k_tid_t)thread, &stats);
|
|
((k_thread_runtime_stats_t *)user_data)->execution_cycles +=
|
|
stats.execution_cycles;
|
|
}
|
|
|
|
/* This case accumulates every thread's execution_cycles first, then
|
|
* get the total execution_cycles from a global
|
|
* k_thread_runtime_stats_t to see that all time is reflected in the
|
|
* total.
|
|
*/
|
|
ZTEST(threads_lifecycle, test_thread_runtime_stats_get)
|
|
{
|
|
k_thread_runtime_stats_t stats, stats_all;
|
|
int ret;
|
|
|
|
stats.execution_cycles = 0;
|
|
|
|
k_thread_foreach(foreach_callback, &stats);
|
|
|
|
/* Check NULL parameters */
|
|
ret = k_thread_runtime_stats_all_get(NULL);
|
|
zassert_true(ret == -EINVAL);
|
|
|
|
k_thread_runtime_stats_all_get(&stats_all);
|
|
|
|
zassert_true(stats.execution_cycles <= stats_all.execution_cycles);
|
|
}
|
|
|
|
ZTEST(threads_lifecycle, test_k_busy_wait)
|
|
{
|
|
uint64_t cycles, dt;
|
|
k_thread_runtime_stats_t test_stats;
|
|
|
|
k_thread_runtime_stats_get(k_current_get(), &test_stats);
|
|
cycles = test_stats.execution_cycles;
|
|
k_busy_wait(0);
|
|
k_thread_runtime_stats_get(k_current_get(), &test_stats);
|
|
|
|
/* execution_cycles doesn't increase significantly after 0
|
|
* usec (10ms slop experimentally determined,
|
|
* non-deterministic software emulators are VERY slow wrt
|
|
* their cycle rate)
|
|
*/
|
|
dt = test_stats.execution_cycles - cycles;
|
|
zassert_true(dt < k_ms_to_cyc_ceil64(10));
|
|
|
|
cycles = test_stats.execution_cycles;
|
|
k_busy_wait(100);
|
|
k_thread_runtime_stats_get(k_current_get(), &test_stats);
|
|
|
|
/* execution_cycles increases correctly */
|
|
dt = test_stats.execution_cycles - cycles;
|
|
|
|
/* execution cycles may not increase by the full 100µs as the
|
|
* system may be doing something else during the busy
|
|
* wait. Experimentally, we see at least 80% of the cycles
|
|
* consumed in the busy wait loop on current test targets.
|
|
*/
|
|
zassert_true(dt >= k_us_to_cyc_floor64(80));
|
|
}
|
|
|
|
static void tp_entry(void *p1, void *p2, void *p3)
|
|
{
|
|
tp = 100;
|
|
}
|
|
|
|
ZTEST_USER(threads_lifecycle_1cpu, test_k_busy_wait_user)
|
|
{
|
|
|
|
k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
|
|
tp_entry, NULL, NULL,
|
|
NULL, 0, K_USER, K_NO_WAIT);
|
|
k_busy_wait(1000);
|
|
/* this is a 1cpu test case, the new thread has no chance to be
|
|
* scheduled and value of tp not changed
|
|
*/
|
|
zassert_false(tp == 100);
|
|
|
|
/* give up cpu, the new thread will change value of tp to 100 */
|
|
k_msleep(100);
|
|
zassert_true(tp == 100);
|
|
k_thread_abort(tid);
|
|
}
|
|
|
|
#define INT_ARRAY_SIZE 128
|
|
static int large_stack(size_t *space)
|
|
{
|
|
/* use "volatile" to protect this variable from being optimized out */
|
|
volatile int a[INT_ARRAY_SIZE];
|
|
|
|
/* to avoid unused variable error */
|
|
a[0] = 1;
|
|
return k_thread_stack_space_get(k_current_get(), space);
|
|
|
|
}
|
|
|
|
static int small_stack(size_t *space)
|
|
{
|
|
return k_thread_stack_space_get(k_current_get(), space);
|
|
}
|
|
|
|
/* test k_thread_stack_sapce_get(), unused stack space in large_stack_space()
|
|
* is smaller than that in small_stack() because the former function has a
|
|
* large local variable
|
|
*/
|
|
ZTEST_USER(threads_lifecycle, test_k_thread_stack_space_get_user)
|
|
{
|
|
size_t a, b;
|
|
|
|
small_stack(&a);
|
|
large_stack(&b);
|
|
/* FIXME: Ideally, the follow condition will assert true:
|
|
* (a - b) == INT_ARRAY_SIZE * sizeof(int)
|
|
* but it is not the case in native_sim, qemu_leon3 and
|
|
* qemu_cortex_a53. Relax check condition here
|
|
*/
|
|
zassert_true(b <= a);
|
|
}
|
|
|
|
static void *thread_test_setup(void)
|
|
{
|
|
k_thread_access_grant(k_current_get(), &tdata, tstack,
|
|
&tdata_custom, tstack_custom,
|
|
&tdata_name, tstack_name,
|
|
&join_thread, join_stack,
|
|
&control_thread, control_stack,
|
|
&deadlock1_thread, deadlock1_stack,
|
|
&deadlock2_thread, deadlock2_stack);
|
|
main_prio = k_thread_priority_get(k_current_get());
|
|
#ifdef CONFIG_USERSPACE
|
|
strncpy(unreadable_string, "unreadable string",
|
|
sizeof(unreadable_string));
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ZTEST_SUITE(threads_lifecycle, NULL, thread_test_setup, NULL, NULL, NULL);
|
|
ZTEST_SUITE(threads_lifecycle_1cpu, NULL, thread_test_setup,
|
|
ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL);
|