blob: 120db96876388d8d19b489dd4d58a2f72af7cebf [file]
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <errno.h>
#include <zephyr/sys/errno_private.h>
#define N_THREADS 2
#define STACK_SIZE (384 + CONFIG_TEST_EXTRA_STACK_SIZE)
static K_THREAD_STACK_ARRAY_DEFINE(stacks, N_THREADS, STACK_SIZE);
static struct k_thread threads[N_THREADS];
K_THREAD_STACK_DEFINE(eno_stack, STACK_SIZE);
struct k_thread eno_thread;
static int errno_values[N_THREADS + 1] = {
0xbabef00d,
0xdeadbeef,
0xabad1dea,
};
struct result {
void *q;
int pass;
};
struct result result[N_THREADS];
struct k_fifo fifo;
static void errno_thread(void *_n, void *_my_errno, void *_unused)
{
int n = POINTER_TO_INT(_n);
int my_errno = POINTER_TO_INT(_my_errno);
errno = my_errno;
k_msleep(30 - (n * 10));
if (errno == my_errno) {
result[n].pass = TC_PASS;
}
zassert_equal(errno, my_errno);
k_fifo_put(&fifo, &result[n]);
}
/**
* @defgroup kernel_errno_tests Error Number
* @ingroup all_tests
* @{
* @}
*
* @addtogroup kernel_errno_tests
* @{
*/
/**
* @brief Verify each thread sees a private errno value preserved across context switches.
*
* @ingroup kernel_errno_tests
*
* @details
* Confirms that errno is thread-local: concurrent threads assigning distinct errno
* values, sleeping for different durations to force interleaving, must each still
* observe their own value when resumed, and the main thread's errno must be
* unaffected. Passing proves errno is saved and restored per thread on context switch.
*
* Test steps:
* - Set the main thread's errno to a known value and record it.
* - Spawn N_THREADS threads, each assigning a unique errno then sleeping a staggered
* interval before re-reading and reporting its result via a FIFO.
* - Collect each thread's pass/fail result and re-check the main thread's errno.
*
* Expected result:
* - Every thread observes its own errno value and the main thread's errno is unchanged.
*
* @see k_thread_create()
*/
ZTEST(common_errno, test_thread_context)
{
int rv = TC_PASS, test_errno;
k_fifo_init(&fifo);
errno = errno_values[N_THREADS];
test_errno = errno;
for (int ii = 0; ii < N_THREADS; ii++) {
result[ii].pass = TC_FAIL;
}
/**TESTPOINT: thread- threads stacks are separate */
for (int ii = 0; ii < N_THREADS; ii++) {
k_thread_create(&threads[ii], stacks[ii], STACK_SIZE,
errno_thread, INT_TO_POINTER(ii),
INT_TO_POINTER(errno_values[ii]), NULL,
K_PRIO_PREEMPT(ii + 5), 0, K_NO_WAIT);
}
for (int ii = 0; ii < N_THREADS; ii++) {
struct result *p = k_fifo_get(&fifo, K_MSEC(100));
if (!p || (p->pass != TC_PASS)) {
rv = TC_FAIL;
}
}
zassert_equal(errno, test_errno);
if (errno != errno_values[N_THREADS]) {
rv = TC_FAIL;
}
/* Make sure all the test thread end. */
for (int ii = 0; ii < N_THREADS; ii++) {
k_thread_join(&threads[ii], K_FOREVER);
}
if (rv != TC_PASS) {
ztest_test_fail();
}
}
#define ERROR_ANY 0xfc
void thread_entry_user(void *p1, void *p2, void *p3)
{
#ifdef CONFIG_NATIVE_LIBC
/* The errno when using the host C library will be handled by it, so we skip it.
*/
ztest_test_skip();
#else
int got_errno;
/* assign the error number to C standard errno */
errno = ERROR_ANY;
/* got errno zephyr stored */
got_errno = *(z_errno());
zassert_equal(errno, got_errno, "errno is not corresponding.");
#endif
}
/**
* @brief Verify the C standard errno and Zephyr's per-thread errno storage stay in sync.
*
* @ingroup kernel_errno_tests
*
* @details
* Confirms that writing to the standard errno updates the same location returned by
* Zephyr's per-thread errno accessor, whether or not thread-local storage is used.
* Passing proves errno reads and writes resolve to Zephyr's per-thread slot. Skipped
* when the native host C library owns errno.
*
* Test steps:
* - From a (possibly user-mode) thread, assign a known value to errno.
* - Read the value back through Zephyr's per-thread errno pointer accessor.
* - Join the spawned thread.
*
* Expected result:
* - The value read via the per-thread accessor equals the value assigned to errno.
*
* @see z_errno()
*/
ZTEST_USER(common_errno, test_errno)
{
k_tid_t tid;
uint32_t perm = K_INHERIT_PERMS;
if (k_is_user_context()) {
perm = perm | K_USER;
}
tid = k_thread_create(&eno_thread, eno_stack, STACK_SIZE,
thread_entry_user, NULL, NULL, NULL,
K_PRIO_PREEMPT(1), perm,
K_NO_WAIT);
k_thread_join(tid, K_FOREVER);
}
/**
* @}
*/
extern void *common_setup(void);
ZTEST_SUITE(common_errno, NULL, common_setup, NULL, NULL, NULL);