Added Dispatcher and fix sim (#54)

- Add dispatcher mechanism (a queue for function calls) and tests
- Added tests for MessageQueue
- Fix FreeRTOS config for simulator
- Explicit dependencies for touch-related libs, because minor version changes caused broken builds on CI.
This commit is contained in:
Ken Van Hoeylandt 2024-08-24 19:21:22 +02:00 committed by GitHub
parent 69a0c01686
commit b659d5b940
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 215 additions and 31 deletions

View File

@ -1,6 +1,6 @@
dependencies: dependencies:
espressif/esp_lcd_ili9341: "^2.0.0" espressif/esp_lcd_ili9341: "2.0.0"
espressif/esp_lcd_touch_cst816s: "^1.0.3" espressif/esp_lcd_touch_cst816s: "1.0.3"
espressif/esp_lcd_touch_gt911: "^1.0.0" espressif/esp_lcd_touch_gt911: "1.1.1"
espressif/esp_lcd_touch: "1.1.1" espressif/esp_lcd_touch: "1.1.2"
idf: '~5.2' idf: '~5.2'

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
extern void vAssertCalled(unsigned long line, const char* const file);
#define configUSE_PREEMPTION 1 #define configUSE_PREEMPTION 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configUSE_TICKLESS_IDLE 0 #define configUSE_TICKLESS_IDLE 0
@ -8,7 +10,6 @@
#define configMINIMAL_STACK_SIZE 128 #define configMINIMAL_STACK_SIZE 128
#define configMAX_TASK_NAME_LEN 16 #define configMAX_TASK_NAME_LEN 16
#define configUSE_16_BIT_TICKS 0 #define configUSE_16_BIT_TICKS 0
#define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_16_BITS
#define configIDLE_SHOULD_YIELD 1 #define configIDLE_SHOULD_YIELD 1
#define configUSE_TASK_NOTIFICATIONS 1 #define configUSE_TASK_NOTIFICATIONS 1
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 2 // Must be the same as ESP32! #define configTASK_NOTIFICATION_ARRAY_ENTRIES 2 // Must be the same as ESP32!
@ -27,16 +28,13 @@
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t #define configMESSAGE_BUFFER_LENGTH_TYPE size_t
#define configHEAP_CLEAR_MEMORY_ON_FREE 1 #define configHEAP_CLEAR_MEMORY_ON_FREE 1
#define configUSE_APPLICATION_TASK_TAG 0 #define configUSE_APPLICATION_TASK_TAG 0
#define configSTATS_BUFFER_MAX_LENGTH 0xFFFF
/* Memory allocation related definitions. */ /* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION 0 #define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configKERNEL_PROVIDED_STATIC_MEMORY 1
#define configTOTAL_HEAP_SIZE (1024 * 1024) #define configTOTAL_HEAP_SIZE (1024 * 1024)
#define configAPPLICATION_ALLOCATED_HEAP 0 #define configAPPLICATION_ALLOCATED_HEAP 0
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 // TODO: Compare with ESP defaults #define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 // TODO: Compare with ESP defaults
#define configENABLE_HEAP_PROTECTOR 1
/* Hook function related definitions. */ /* Hook function related definitions. */
#define configUSE_IDLE_HOOK 0 #define configUSE_IDLE_HOOK 0
@ -61,29 +59,11 @@
#define configTIMER_QUEUE_LENGTH 10 #define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
/* Interrupt nesting behaviour configuration. */
#define configKERNEL_INTERRUPT_PRIORITY 1
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 4
#define configMAX_API_CALL_INTERRUPT_PRIORITY 10
/* Define to trap errors during development. */ /* Define to trap errors during development. */
#define configASSERT(x) if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ ) #define configASSERT(x) if( ( x ) == 0 ) vAssertCalled(__LINE__, __FILE__)
/* FreeRTOS MPU specific definitions. */ /* FreeRTOS MPU specific definitions. */
#define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0 #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0
#define configTOTAL_MPU_REGIONS 8 /* Default value. */
#define configTEX_S_C_B_FLASH 0x07UL /* Default value. */
#define configTEX_S_C_B_SRAM 0x07UL /* Default value. */
#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY 1
#define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS 1
#define configENABLE_ERRATA_837070_WORKAROUND 1
#define configUSE_MPU_WRAPPERS_V1 0
#define configPROTECTED_KERNEL_OBJECT_POOL_SIZE 10
#define configSYSTEM_CALL_STACK_SIZE 128
#define configENABLE_ACCESS_CONTROL_LIST 1
/* ARMv8-M secure side port related definitions. */
#define secureconfigMAX_SECURE_CONTEXTS 5
/* Optional functions - most linkers will remove unused functions anyway. */ /* Optional functions - most linkers will remove unused functions anyway. */
/* Ensure these are closely match ESP32: you can activate more features, but not less. */ /* Ensure these are closely match ESP32: you can activate more features, but not less. */
@ -91,7 +71,6 @@
#define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskSuspend 1
#define INCLUDE_xResumeFromISR 1
#define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1 #define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTaskGetSchedulerState 1
@ -100,7 +79,6 @@
#define INCLUDE_uxTaskGetStackHighWaterMark2 1 #define INCLUDE_uxTaskGetStackHighWaterMark2 1
#define INCLUDE_xTaskGetIdleTaskHandle 1 #define INCLUDE_xTaskGetIdleTaskHandle 1
#define INCLUDE_eTaskGetState 1 #define INCLUDE_eTaskGetState 1
#define INCLUDE_xEventGroupSetBitFromISR 1
#define INCLUDE_xTimerPendFunctionCall 1 #define INCLUDE_xTimerPendFunctionCall 1
#define INCLUDE_xTaskAbortDelay 1 #define INCLUDE_xTaskAbortDelay 1
#define INCLUDE_xTaskGetHandle 1 #define INCLUDE_xTaskGetHandle 1

View File

@ -0,0 +1,48 @@
#include "dispatcher.h"
#include "tactility_core.h"
Dispatcher* tt_dispatcher_alloc(uint32_t message_count) {
Dispatcher* dispatcher = malloc(sizeof(Dispatcher));
*dispatcher = (Dispatcher) {
.queue = tt_message_queue_alloc(message_count, sizeof(DispatcherMessage)),
.mutex = tt_mutex_alloc(MutexTypeNormal),
.buffer = {
.callback = NULL,
.context = NULL
}
};
return dispatcher;
}
void tt_dispatcher_free(Dispatcher* dispatcher) {
tt_mutex_acquire(dispatcher->mutex, TtWaitForever);
tt_message_queue_reset(dispatcher->queue);
tt_message_queue_free(dispatcher->queue);
tt_mutex_release(dispatcher->mutex);
tt_mutex_free(dispatcher->mutex);
free(dispatcher);
}
void tt_dispatcher_dispatch(Dispatcher* dispatcher, Callback callback, void* context) {
DispatcherMessage message = {
.callback = callback,
.context = context
};
tt_mutex_acquire(dispatcher->mutex, TtWaitForever);
tt_message_queue_put(dispatcher->queue, &message, TtWaitForever);
tt_mutex_release(dispatcher->mutex);
}
bool tt_dispatcher_consume(Dispatcher* dispatcher, uint32_t timeout_ticks) {
tt_mutex_acquire(dispatcher->mutex, TtWaitForever);
if (tt_message_queue_get(dispatcher->queue, &(dispatcher->buffer), timeout_ticks) == TtStatusOk) {
DispatcherMessage* message = &(dispatcher->buffer);
message->callback(message->context);
tt_mutex_release(dispatcher->mutex);
return true;
} else {
tt_mutex_release(dispatcher->mutex);
return false;
}
}

View File

@ -0,0 +1,36 @@
/**
* @file message_queue.h
*
* Dispatcher is a thread-safe message queue implementation for callbacks.
*/
#pragma once
#include "message_queue.h"
#include "mutex.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*Callback)(void* data);
typedef struct {
Callback callback;
void* context;
} DispatcherMessage;
typedef struct {
MessageQueue* queue;
Mutex* mutex;
DispatcherMessage buffer; // Buffer for consuming a message
} Dispatcher;
Dispatcher* tt_dispatcher_alloc(uint32_t message_count);
void tt_dispatcher_free(Dispatcher* dispatcher);
void tt_dispatcher_dispatch(Dispatcher* dispatcher, Callback callback, void* context);
bool tt_dispatcher_consume(Dispatcher* dispatcher, uint32_t timeout_ticks);
#ifdef __cplusplus
}
#endif

View File

@ -1,6 +1,9 @@
/** /**
* @file message_queue.h * @file message_queue.h
* MessageQueue *
* MessageQueue is a wrapper for FreeRTOS xQueue functionality.
* There is no additional thread-safety on top of the xQueue functionality,
* so make sure you create a lock if needed.
*/ */
#pragma once #pragma once

View File

@ -72,7 +72,7 @@ static void register_user_apps(const AppManifest* const apps[TT_CONFIG_APPS_LIMI
static void register_and_start_system_services() { static void register_and_start_system_services() {
TT_LOG_I(TAG, "Registering and starting system services"); TT_LOG_I(TAG, "Registering and starting system services");
int app_count = sizeof(system_services) / sizeof(ServiceManifest *); int app_count = sizeof(system_services) / sizeof(ServiceManifest*);
for (int i = 0; i < app_count; ++i) { for (int i = 0; i < app_count; ++i) {
tt_service_registry_add(system_services[i]); tt_service_registry_add(system_services[i]);
tt_check(tt_service_registry_start(system_services[i]->id)); tt_check(tt_service_registry_start(system_services[i]->id));

View File

@ -0,0 +1,38 @@
#include "doctest.h"
#include "tactility_core.h"
#include "dispatcher.h"
void increment_callback(void* context) {
auto* counter = (uint32_t*)context;
(*counter)++;
}
TEST_CASE("dispatcher should not call callback if consume isn't called") {
Dispatcher* dispatcher = tt_dispatcher_alloc(10);
uint32_t counter = 0;
tt_dispatcher_dispatch(dispatcher, &increment_callback, &counter);
tt_delay_tick(10);
CHECK_EQ(counter, 0);
tt_dispatcher_free(dispatcher);
}
TEST_CASE("dispatcher should be able to dealloc when message is not consumed") {
Dispatcher* dispatcher = tt_dispatcher_alloc(10);
uint32_t counter = 0;
tt_dispatcher_dispatch(dispatcher, increment_callback, &counter);
tt_dispatcher_free(dispatcher);
}
TEST_CASE("dispatcher should call callback when consume is called") {
Dispatcher* dispatcher = tt_dispatcher_alloc(10);
uint32_t counter = 0;
tt_dispatcher_dispatch(dispatcher, increment_callback, &counter);
tt_dispatcher_consume(dispatcher, 100);
CHECK_EQ(counter, 1);
tt_dispatcher_free(dispatcher);
}

View File

@ -0,0 +1,81 @@
#include "doctest.h"
#include "message_queue.h"
TEST_CASE("message queue capacity should be correct") {
MessageQueue* queue = tt_message_queue_alloc(10, 1);
uint32_t capacity = tt_message_queue_get_capacity(queue);
CHECK_EQ(capacity, 10);
tt_message_queue_free(queue);
}
TEST_CASE("message queue initial count should be 0") {
MessageQueue* queue = tt_message_queue_alloc(10, 1);
uint32_t count = tt_message_queue_get_count(queue);
CHECK_EQ(count, 0);
tt_message_queue_free(queue);
}
TEST_CASE("message queue size should be correct") {
MessageQueue* queue = tt_message_queue_alloc(1, 123);
uint32_t size = tt_message_queue_get_message_size(queue);
CHECK_EQ(size, 123);
tt_message_queue_free(queue);
}
TEST_CASE("message queue count should increase when message is added") {
MessageQueue* queue = tt_message_queue_alloc(10, sizeof(uint32_t));
uint32_t message = 123;
tt_message_queue_put(queue, &message, 100);
uint32_t count = tt_message_queue_get_count(queue);
CHECK_EQ(count, 1);
tt_message_queue_free(queue);
}
TEST_CASE("message queue count should be 0 when message is added and queue is reset") {
MessageQueue* queue = tt_message_queue_alloc(10, sizeof(uint32_t));
uint32_t message = 123;
tt_message_queue_put(queue, &message, 100);
tt_message_queue_reset(queue);
uint32_t count = tt_message_queue_get_count(queue);
CHECK_EQ(count, 0);
tt_message_queue_free(queue);
}
TEST_CASE("message queue consumption should work") {
MessageQueue* queue = tt_message_queue_alloc(10, sizeof(uint32_t));
uint32_t out_message = 123;
tt_message_queue_put(queue, &out_message, 100);
uint32_t in_message = 0;
tt_message_queue_get(queue, &in_message, 100);
CHECK_EQ(in_message, 123);
tt_message_queue_free(queue);
}
TEST_CASE("message queue count should decrease when message is consumed") {
MessageQueue* queue = tt_message_queue_alloc(10, sizeof(uint32_t));
uint32_t out_message = 123;
tt_message_queue_put(queue, &out_message, 100);
uint32_t in_message = 0;
tt_message_queue_get(queue, &in_message, 100);
uint32_t count = tt_message_queue_get_count(queue);
CHECK_EQ(count, 0);
tt_message_queue_free(queue);
}