TactilityKernel improvements and more (#459)

This commit is contained in:
Ken Van Hoeylandt
2026-01-25 23:54:33 +01:00
committed by GitHub
parent 96eccbdc8d
commit dfe2c865d1
36 changed files with 759 additions and 331 deletions
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: Apache-2.0
/**
* Dispatcher is a thread-safe code execution queue.
*/
#pragma once
#include <Tactility/Error.h>
#include <Tactility/FreeRTOS/FreeRTOS.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*DispatcherCallback)(void* context);
typedef void* DispatcherHandle_t;
DispatcherHandle_t dispatcher_alloc(void);
void dispatcher_free(DispatcherHandle_t dispatcher);
/**
* Queue a function to be consumed elsewhere.
*
* @param[in] callbackContext the data to pass to the function upon execution
* @param[in] callback the function to execute elsewhere
* @param[in] timeout lock acquisition timeout
* @retval ERROR_TIMEOUT
* @retval ERROR_RESOURCE when failing to set event
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
* @retval ERROR_NONE
*/
error_t dispatcher_dispatch_timed(DispatcherHandle_t dispatcher, void* callbackContext, DispatcherCallback callback, TickType_t timeout);
/**
* Queue a function to be consumed elsewhere.
*
* @param[in] callbackContext the data to pass to the function upon execution
* @param[in] callback the function to execute elsewhere
* @retval ERROR_RESOURCE when failing to set event
* @retval ERROR_TIMEOUT unlikely to occur unless there's an issue with the internal mutex
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
* @retval ERROR_NONE
*/
static inline error_t dispatcher_dispatch(DispatcherHandle_t dispatcher, void* callbackContext, DispatcherCallback callback) {
return dispatcher_dispatch_timed(dispatcher, callbackContext, callback, portMAX_DELAY);
}
/**
* Consume 1 or more dispatched functions (if any) until the queue is empty.
*
* @warning The timeout is only the wait time before consuming the message! It is not a limit to the total execution time when calling this method.
*
* @param[in] timeout the ticks to wait for a message
* @retval ERROR_TIMEOUT
* @retval ERROR_RESOURCE failed to wait for event
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
* @retval ERROR_NONE
*/
error_t dispatcher_consume_timed(DispatcherHandle_t dispatcher, TickType_t timeout);
/**
* Consume 1 or more dispatched functions (if any) until the queue is empty.
*
* @warning The timeout is only the wait time before consuming the message! It is not a limit to the total execution time when calling this method.
*
* @retval ERROR_TIMEOUT unlikely to occur unless there's an issue with the internal mutex
* @retval ERROR_RESOURCE failed to wait for event
* @retval ERROR_INVALID_STATE when the dispatcher is in the process of shutting down
* @retval ERROR_NONE
*/
static inline error_t dispatcher_consume(DispatcherHandle_t dispatcher) {
return dispatcher_consume_timed(dispatcher, portMAX_DELAY);
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <Tactility/Error.h>
#include <Tactility/FreeRTOS/event_groups.h>
static inline void event_group_construct(EventGroupHandle_t* eventGroup) {
assert(xPortInIsrContext() == pdFALSE);
*eventGroup = xEventGroupCreate();
}
static inline void event_group_destruct(EventGroupHandle_t* eventGroup) {
assert(xPortInIsrContext() == pdFALSE);
assert(*eventGroup != NULL);
vEventGroupDelete(*eventGroup);
*eventGroup = NULL;
}
/**
* Set the flags.
*
* @param[in] eventGroup the event group
* @param[in] flags the flags to set
* @retval ERROR_RESOURCE when setting failed
* @retval ERROR_NONE
*/
error_t event_group_set(EventGroupHandle_t eventGroup, uint32_t flags);
/**
* Clear flags
*
* @param[in] eventGroup the event group
* @param[in] flags the flags to clear
* @retval ERROR_RESOURCE when clearing failed
* @retval ERROR_NONE
*/
error_t event_group_clear(EventGroupHandle_t eventGroup, uint32_t flags);
/**
* @param[in] eventGroup the event group
* @return the bitset (always succeeds)
*/
uint32_t event_group_get(EventGroupHandle_t eventGroup);
/**
* Wait for flags to be set
*
* @param[in] eventGroup the event group
* @param[in] inFlags the flags to await
* @param[in] awaitAll If true, await for all bits to be set. Otherwise, await for any.
* @param[in] clearOnExit If true, clears all the bits on exit, otherwise don't clear.
* @param[out] outFlags If set to non-NULL value, this will hold the resulting flags. Only set when return value is ERROR_NONE
* @param[in] timeout the maximum amount of ticks to wait for flags to be set
* @retval ERROR_ISR_STATUS when the function was called from an ISR context
* @retval ERROR_TIMEOUT
* @retval ERROR_RESOURCE when flags were triggered, but not in a way that was expected (e.g. waiting for all flags, but was only partially set)
* @retval ERROR_NONE
*/
error_t event_group_wait(
EventGroupHandle_t eventGroup,
uint32_t inFlags,
bool awaitAll,
bool clearOnExit,
uint32_t* outFlags,
TickType_t timeout
);
#ifdef __cplusplus
}
#endif
@@ -12,6 +12,7 @@ extern "C" {
struct Mutex {
QueueHandle_t handle;
// TODO: Debugging functionality
};
inline static void mutex_construct(struct Mutex* mutex) {
@@ -27,18 +28,30 @@ inline static void mutex_destruct(struct Mutex* mutex) {
}
inline static void mutex_lock(struct Mutex* mutex) {
assert(xPortInIsrContext() != pdTRUE);
xSemaphoreTake(mutex->handle, portMAX_DELAY);
}
inline static bool mutex_try_lock(struct Mutex* mutex) {
assert(xPortInIsrContext() != pdTRUE);
return xSemaphoreTake(mutex->handle, 0) == pdTRUE;
}
inline static bool mutex_try_lock_timed(struct Mutex* mutex, TickType_t timeout) {
assert(xPortInIsrContext() != pdTRUE);
return xSemaphoreTake(mutex->handle, timeout) == pdTRUE;
}
inline static bool mutex_is_locked(struct Mutex* mutex) {
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
if (xPortInIsrContext() == pdTRUE) {
return xSemaphoreGetMutexHolderFromISR(mutex->handle) != NULL;
} else {
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
}
}
inline static void mutex_unlock(struct Mutex* mutex) {
assert(xPortInIsrContext() != pdTRUE);
xSemaphoreGive(mutex->handle);
}
@@ -26,18 +26,30 @@ inline static void recursive_mutex_destruct(struct RecursiveMutex* mutex) {
}
inline static void recursive_mutex_lock(struct RecursiveMutex* mutex) {
assert(xPortInIsrContext() != pdTRUE);
xSemaphoreTakeRecursive(mutex->handle, portMAX_DELAY);
}
inline static bool recursive_mutex_is_locked(struct RecursiveMutex* mutex) {
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
if (xPortInIsrContext() == pdTRUE) {
return xSemaphoreGetMutexHolderFromISR(mutex->handle) != NULL;
} else {
return xSemaphoreGetMutexHolder(mutex->handle) != NULL;
}
}
inline static bool recursive_mutex_try_lock(struct RecursiveMutex* mutex) {
assert(xPortInIsrContext() != pdTRUE);
return xSemaphoreTakeRecursive(mutex->handle, 0) == pdTRUE;
}
inline static bool recursive_mutex_try_lock_timed(struct RecursiveMutex* mutex, TickType_t timeout) {
assert(xPortInIsrContext() != pdTRUE);
return xSemaphoreTakeRecursive(mutex->handle, timeout) == pdTRUE;
}
inline static void recursive_mutex_unlock(struct RecursiveMutex* mutex) {
assert(xPortInIsrContext() != pdTRUE);
xSemaphoreGiveRecursive(mutex->handle);
}