esp-idf/components/pthread/test_apps/pthread_unity_tests/main/test_pthread.c

328 wiersze
9.3 KiB
C

/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <errno.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_pthread.h"
#include <pthread.h>
#include "unity.h"
TEST_CASE("esp_pthread_get_default_config creates correct stack memory capabilities", "[set_cfg]")
{
esp_pthread_cfg_t default_config = esp_pthread_get_default_config();
// The default must always be internal, 8-bit accessible RAM
TEST_ASSERT_EQUAL_HEX(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL, default_config.stack_alloc_caps);
}
TEST_CASE("wrong heap caps are rejected", "[set_cfg]")
{
esp_pthread_cfg_t default_config = esp_pthread_get_default_config();
default_config.stack_alloc_caps = MALLOC_CAP_32BIT;
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_pthread_set_cfg(&default_config));
default_config.stack_alloc_caps = MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL;
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_pthread_set_cfg(&default_config));
}
TEST_CASE("correct memory is accepted", "[set_cfg]")
{
esp_pthread_cfg_t default_config = esp_pthread_get_default_config();
default_config.stack_alloc_caps = MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL;
TEST_ASSERT_EQUAL(ESP_OK, esp_pthread_set_cfg(&default_config));
}
static void *compute_square(void *arg)
{
int *num = (int *) arg;
*num = (*num) * (*num);
vTaskDelay(2); // ensure the test task has time to continue execution
pthread_exit((void *) num);
return NULL;
}
TEST_CASE("pthread create join", "[pthread]")
{
int res = 0;
volatile int num = 7;
volatile bool attr_init = false;
void *thread_rval = NULL;
pthread_t new_thread = (pthread_t)NULL;
pthread_attr_t attr;
if (TEST_PROTECT()) {
res = pthread_attr_init(&attr);
TEST_ASSERT_EQUAL_INT(0, res);
attr_init = true;
res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_create(&new_thread, &attr, compute_square, (void *) &num);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_join(new_thread, &thread_rval);
TEST_ASSERT_EQUAL_INT(EDEADLK, res);
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ASSERT_EQUAL_INT(49, num);
res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_create(&new_thread, &attr, compute_square, (void *) &num);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_join(new_thread, &thread_rval);
TEST_ASSERT_EQUAL_INT(0, res);
TEST_ASSERT_EQUAL_INT(2401, num);
TEST_ASSERT_EQUAL_PTR(&num, thread_rval);
}
if (attr_init) {
pthread_attr_destroy(&attr);
}
}
static void *waiting_thread(void *arg)
{
TaskHandle_t *task_handle = (TaskHandle_t *)arg;
TaskHandle_t parent_task = *task_handle;
*task_handle = xTaskGetCurrentTaskHandle();
xTaskNotify(parent_task, 0, eNoAction);
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
return NULL;
}
TEST_CASE("pthread detach", "[pthread]")
{
int res = 0;
pthread_t new_thread = (pthread_t)NULL;
TaskHandle_t task_handle = NULL;
const int task_count = uxTaskGetNumberOfTasks();
bool detach_works = false;
if (TEST_PROTECT()) {
task_handle = xTaskGetCurrentTaskHandle();
res = pthread_create(&new_thread, NULL, waiting_thread, (void *)&task_handle);
TEST_ASSERT_EQUAL_INT(0, res);
res = xTaskNotifyWait(0, 0, NULL, 100 / portTICK_PERIOD_MS);
TEST_ASSERT_EQUAL_INT(pdTRUE, res);
xTaskNotify(task_handle, 0, eNoAction);
vTaskDelay(100 / portTICK_PERIOD_MS);
res = pthread_detach(new_thread);
TEST_ASSERT_EQUAL_INT(0, res);
res = uxTaskGetNumberOfTasks();
TEST_ASSERT_EQUAL_INT(task_count, res);
detach_works = true;
}
if (!detach_works) {
vTaskDelete(task_handle);
} else {
detach_works = false;
}
if (TEST_PROTECT()) {
task_handle = xTaskGetCurrentTaskHandle();
res = pthread_create(&new_thread, NULL, waiting_thread, (void *)&task_handle);
TEST_ASSERT_EQUAL_INT(0, res);
res = xTaskNotifyWait(0, 0, NULL, 100 / portTICK_PERIOD_MS);
TEST_ASSERT_EQUAL_INT(pdTRUE, res);
res = pthread_detach(new_thread);
TEST_ASSERT_EQUAL_INT(0, res);
xTaskNotify(task_handle, 0, eNoAction);
vTaskDelay(100 / portTICK_PERIOD_MS);
res = uxTaskGetNumberOfTasks();
TEST_ASSERT_EQUAL_INT(task_count, res);
detach_works = true;
}
if (!detach_works) {
vTaskDelete(task_handle);
}
}
TEST_CASE("pthread attr init destroy", "[pthread]")
{
int res = 0;
size_t stack_size_1 = 0, stack_size_2 = 0;
volatile bool attr_init = pdFALSE;
pthread_attr_t attr;
if (TEST_PROTECT()) {
res = pthread_attr_init(&attr);
TEST_ASSERT_EQUAL_INT(0, res);
attr_init = true;
res = pthread_attr_getstacksize(&attr, &stack_size_1);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_attr_setstacksize(&attr, stack_size_1);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_attr_getstacksize(&attr, &stack_size_2);
TEST_ASSERT_EQUAL_INT(0, res);
TEST_ASSERT_EQUAL_INT(stack_size_2, stack_size_1);
stack_size_1 = PTHREAD_STACK_MIN - 1;
res = pthread_attr_setstacksize(&attr, stack_size_1);
TEST_ASSERT_EQUAL_INT(EINVAL, res);
}
if (attr_init) {
TEST_ASSERT_EQUAL_INT(0, pthread_attr_destroy(&attr));
}
}
static void *unlock_mutex(void *arg)
{
pthread_mutex_t *mutex = (pthread_mutex_t *) arg;
intptr_t res = (intptr_t) pthread_mutex_unlock(mutex);
pthread_exit((void *) res);
return NULL;
}
static void test_mutex_lock_unlock(int mutex_type)
{
int res = 0;
int set_type = -1;
volatile bool attr_created = false;
volatile bool mutex_created = false;
volatile intptr_t thread_rval = 0;
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_t new_thread;
if (TEST_PROTECT()) {
res = pthread_mutexattr_init(&attr);
TEST_ASSERT_EQUAL_INT(0, res);
attr_created = true;
res = pthread_mutexattr_settype(&attr, mutex_type);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_mutexattr_gettype(&attr, &set_type);
TEST_ASSERT_EQUAL_INT(0, res);
TEST_ASSERT_EQUAL_INT(mutex_type, set_type);
res = pthread_mutex_init(&mutex, &attr);
TEST_ASSERT_EQUAL_INT(0, res);
mutex_created = true;
res = pthread_mutex_lock(&mutex);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_mutex_lock(&mutex);
if (mutex_type == PTHREAD_MUTEX_ERRORCHECK) {
TEST_ASSERT_EQUAL_INT(EDEADLK, res);
} else {
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_mutex_unlock(&mutex);
TEST_ASSERT_EQUAL_INT(0, res);
}
pthread_create(&new_thread, NULL, unlock_mutex, &mutex);
pthread_join(new_thread, (void **) &thread_rval);
TEST_ASSERT_EQUAL_INT(EPERM, (int) thread_rval);
res = pthread_mutex_unlock(&mutex);
TEST_ASSERT_EQUAL_INT(0, res);
}
if (attr_created) {
pthread_mutexattr_destroy(&attr);
}
if (mutex_created) {
pthread_mutex_destroy(&mutex);
}
}
TEST_CASE("pthread mutex lock unlock", "[pthread]")
{
int res = 0;
/* Present behavior of mutex initializer is unlike what is
* defined in Posix standard, ie. calling pthread_mutex_lock
* on such a mutex would internally cause dynamic allocation.
* Therefore pthread_mutex_destroy needs to be called in
* order to avoid memory leak. */
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
res = pthread_mutex_lock(&mutex);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_mutex_unlock(&mutex);
TEST_ASSERT_EQUAL_INT(0, res);
/* This deviates from the Posix standard static mutex behavior.
* This needs to be removed in the future when standard mutex
* initializer is supported */
pthread_mutex_destroy(&mutex);
test_mutex_lock_unlock(PTHREAD_MUTEX_ERRORCHECK);
test_mutex_lock_unlock(PTHREAD_MUTEX_RECURSIVE);
}
static void timespec_add_nano(struct timespec * out, struct timespec * in, long val)
{
out->tv_nsec = val + in->tv_nsec;
if (out->tv_nsec < (in->tv_nsec)) {
out->tv_sec += 1;
}
}
TEST_CASE("pthread mutex trylock timedlock", "[pthread]")
{
int res = 0;
volatile bool mutex_created = false;
pthread_mutex_t mutex;
struct timespec abs_timeout;
if (TEST_PROTECT()) {
res = pthread_mutex_init(&mutex, NULL);
TEST_ASSERT_EQUAL_INT(0, res);
mutex_created = true;
res = pthread_mutex_trylock(&mutex);
TEST_ASSERT_EQUAL_INT(0, res);
res = pthread_mutex_trylock(&mutex);
TEST_ASSERT_EQUAL_INT(EBUSY, res);
clock_gettime(CLOCK_REALTIME, &abs_timeout);
timespec_add_nano(&abs_timeout, &abs_timeout, 100000000LL);
res = pthread_mutex_timedlock(&mutex, &abs_timeout);
TEST_ASSERT_EQUAL_INT(ETIMEDOUT, res);
res = pthread_mutex_unlock(&mutex);
TEST_ASSERT_EQUAL_INT(0, res);
}
if (mutex_created) {
pthread_mutex_destroy(&mutex);
}
}