mirror of
https://github.com/flipperdevices/flipperzero-firmware.git
synced 2025-12-12 04:41:26 +04:00
[FL-3832] Use static synchronisation primitives (#3679)
* Use static mutex * Add static_assert checks * Use static semaphore * Fix formatting * Use static stream buffer * Use static timer * Use static event group * Increase allocation size for stream buffer * Remove recursive bit from the mutex before freeing * Prevent service tasks from ever returning * Use static threads * Do not realloc memory when changing stack size * Use FuriSemaphore instead of raw FreeRTOS one in rpc_test * Remove redundant includes * Abolish FreeRTOS dynamic allocation * Improve FuriMutex * Improve FuriMessageQueue * Remove redundant comments and parentheses * Clean up code more * Create service threads via a dedicated constructor * Minor code improvements * Update docs for FuriThread, FuriTimer * Fix doxygen typo * Use a bigger buffer for static StreamBuffer * Furi: remove timer control block only when timer thread have completed all operations --------- Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
@@ -8,18 +8,27 @@
|
||||
#define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U
|
||||
#define FURI_EVENT_FLAG_INVALID_BITS (~((1UL << FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U))
|
||||
|
||||
struct FuriEventFlag {
|
||||
StaticEventGroup_t container;
|
||||
};
|
||||
|
||||
// IMPORTANT: container MUST be the FIRST struct member
|
||||
static_assert(offsetof(FuriEventFlag, container) == 0);
|
||||
|
||||
FuriEventFlag* furi_event_flag_alloc(void) {
|
||||
furi_check(!FURI_IS_IRQ_MODE());
|
||||
|
||||
EventGroupHandle_t handle = xEventGroupCreate();
|
||||
furi_check(handle);
|
||||
FuriEventFlag* instance = malloc(sizeof(FuriEventFlag));
|
||||
|
||||
return ((FuriEventFlag*)handle);
|
||||
furi_check(xEventGroupCreateStatic(&instance->container) == (EventGroupHandle_t)instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void furi_event_flag_free(FuriEventFlag* instance) {
|
||||
furi_check(!FURI_IS_IRQ_MODE());
|
||||
vEventGroupDelete((EventGroupHandle_t)instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) {
|
||||
@@ -43,7 +52,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) {
|
||||
}
|
||||
|
||||
/* Return event flags after setting */
|
||||
return (rflags);
|
||||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) {
|
||||
@@ -69,7 +78,7 @@ uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) {
|
||||
}
|
||||
|
||||
/* Return event flags before clearing */
|
||||
return (rflags);
|
||||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t furi_event_flag_get(FuriEventFlag* instance) {
|
||||
@@ -85,7 +94,7 @@ uint32_t furi_event_flag_get(FuriEventFlag* instance) {
|
||||
}
|
||||
|
||||
/* Return current event flags */
|
||||
return (rflags);
|
||||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t furi_event_flag_wait(
|
||||
@@ -136,5 +145,5 @@ uint32_t furi_event_flag_wait(
|
||||
}
|
||||
|
||||
/* Return event flags before clearing */
|
||||
return (rflags);
|
||||
return rflags;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void FuriEventFlag;
|
||||
typedef struct FuriEventFlag FuriEventFlag;
|
||||
|
||||
/** Allocate FuriEventFlag
|
||||
*
|
||||
|
||||
@@ -56,10 +56,6 @@ task.h is included from an application file. */
|
||||
#error This feature is broken, logging transport must be replaced with RTT
|
||||
#endif
|
||||
|
||||
#if(configSUPPORT_DYNAMIC_ALLOCATION == 0)
|
||||
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
|
||||
#endif
|
||||
|
||||
/* Block sizes must not get too small. */
|
||||
#define heapMINIMUM_BLOCK_SIZE ((size_t)(xHeapStructSize << 1))
|
||||
|
||||
|
||||
@@ -5,14 +5,21 @@
|
||||
#include <FreeRTOS.h>
|
||||
#include <queue.h>
|
||||
|
||||
struct FuriMessageQueue {
|
||||
// !!! Semi-Opaque type inheritance, Very Fragile, DO NOT MOVE !!!
|
||||
StaticQueue_t container;
|
||||
// Internal FreeRTOS member names
|
||||
#define uxMessagesWaiting uxDummy4[0]
|
||||
#define uxLength uxDummy4[1]
|
||||
#define uxItemSize uxDummy4[2]
|
||||
|
||||
// !!! Data buffer, must be last in the structure, DO NOT MOVE !!!
|
||||
struct FuriMessageQueue {
|
||||
StaticQueue_t container;
|
||||
uint8_t buffer[];
|
||||
};
|
||||
|
||||
// IMPORTANT: container MUST be the FIRST struct member
|
||||
static_assert(offsetof(FuriMessageQueue, container) == 0);
|
||||
// IMPORTANT: buffer MUST be the LAST struct member
|
||||
static_assert(offsetof(FuriMessageQueue, buffer) == sizeof(FuriMessageQueue));
|
||||
|
||||
FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) {
|
||||
furi_check((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U));
|
||||
|
||||
@@ -75,8 +82,7 @@ FuriStatus
|
||||
}
|
||||
}
|
||||
|
||||
/* Return execution status */
|
||||
return (stat);
|
||||
return stat;
|
||||
}
|
||||
|
||||
FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uint32_t timeout) {
|
||||
@@ -114,31 +120,19 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin
|
||||
}
|
||||
}
|
||||
|
||||
return (stat);
|
||||
return stat;
|
||||
}
|
||||
|
||||
uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
uint32_t capacity;
|
||||
|
||||
/* capacity = pxQueue->uxLength */
|
||||
capacity = instance->container.uxDummy4[1];
|
||||
|
||||
/* Return maximum number of messages */
|
||||
return (capacity);
|
||||
return instance->container.uxLength;
|
||||
}
|
||||
|
||||
uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
uint32_t size;
|
||||
|
||||
/* size = pxQueue->uxItemSize */
|
||||
size = instance->container.uxDummy4[2];
|
||||
|
||||
/* Return maximum message size */
|
||||
return (size);
|
||||
return instance->container.uxItemSize;
|
||||
}
|
||||
|
||||
uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) {
|
||||
@@ -153,8 +147,7 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) {
|
||||
count = uxQueueMessagesWaiting(hQueue);
|
||||
}
|
||||
|
||||
/* Return number of queued messages */
|
||||
return ((uint32_t)count);
|
||||
return (uint32_t)count;
|
||||
}
|
||||
|
||||
uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {
|
||||
@@ -166,16 +159,14 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {
|
||||
if(furi_kernel_is_irq_or_masked() != 0U) {
|
||||
isrm = taskENTER_CRITICAL_FROM_ISR();
|
||||
|
||||
/* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */
|
||||
space = instance->container.uxDummy4[1] - instance->container.uxDummy4[0];
|
||||
space = instance->container.uxLength - instance->container.uxMessagesWaiting;
|
||||
|
||||
taskEXIT_CRITICAL_FROM_ISR(isrm);
|
||||
} else {
|
||||
space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)instance);
|
||||
}
|
||||
|
||||
/* Return number of available slots */
|
||||
return (space);
|
||||
return space;
|
||||
}
|
||||
|
||||
FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) {
|
||||
@@ -191,6 +182,5 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) {
|
||||
(void)xQueueReset(hQueue);
|
||||
}
|
||||
|
||||
/* Return execution status */
|
||||
return (stat);
|
||||
return stat;
|
||||
}
|
||||
|
||||
@@ -5,129 +5,120 @@
|
||||
#include <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
|
||||
// Internal FreeRTOS member names
|
||||
#define ucQueueType ucDummy9
|
||||
|
||||
struct FuriMutex {
|
||||
StaticSemaphore_t container;
|
||||
};
|
||||
|
||||
// IMPORTANT: container MUST be the FIRST struct member
|
||||
static_assert(offsetof(FuriMutex, container) == 0);
|
||||
|
||||
FuriMutex* furi_mutex_alloc(FuriMutexType type) {
|
||||
furi_check(!FURI_IS_IRQ_MODE());
|
||||
|
||||
SemaphoreHandle_t hMutex = NULL;
|
||||
FuriMutex* instance = malloc(sizeof(FuriMutex));
|
||||
|
||||
SemaphoreHandle_t hMutex;
|
||||
|
||||
if(type == FuriMutexTypeNormal) {
|
||||
hMutex = xSemaphoreCreateMutex();
|
||||
hMutex = xSemaphoreCreateMutexStatic(&instance->container);
|
||||
} else if(type == FuriMutexTypeRecursive) {
|
||||
hMutex = xSemaphoreCreateRecursiveMutex();
|
||||
hMutex = xSemaphoreCreateRecursiveMutexStatic(&instance->container);
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
|
||||
furi_check(hMutex != NULL);
|
||||
furi_check(hMutex == (SemaphoreHandle_t)instance);
|
||||
|
||||
if(type == FuriMutexTypeRecursive) {
|
||||
/* Set LSB as 'recursive mutex flag' */
|
||||
hMutex = (SemaphoreHandle_t)((uint32_t)hMutex | 1U);
|
||||
}
|
||||
|
||||
/* Return mutex ID */
|
||||
return ((FuriMutex*)hMutex);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void furi_mutex_free(FuriMutex* instance) {
|
||||
furi_check(!FURI_IS_IRQ_MODE());
|
||||
furi_check(instance);
|
||||
|
||||
vSemaphoreDelete((SemaphoreHandle_t)((uint32_t)instance & ~1U));
|
||||
vSemaphoreDelete((SemaphoreHandle_t)instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) {
|
||||
furi_check(instance);
|
||||
|
||||
SemaphoreHandle_t hMutex;
|
||||
FuriStatus stat;
|
||||
uint32_t rmtx;
|
||||
SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance);
|
||||
const uint8_t mutex_type = instance->container.ucQueueType;
|
||||
|
||||
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
|
||||
|
||||
/* Extract recursive mutex flag */
|
||||
rmtx = (uint32_t)instance & 1U;
|
||||
|
||||
stat = FuriStatusOk;
|
||||
FuriStatus stat = FuriStatusOk;
|
||||
|
||||
if(FURI_IS_IRQ_MODE()) {
|
||||
stat = FuriStatusErrorISR;
|
||||
} else if(hMutex == NULL) {
|
||||
stat = FuriStatusErrorParameter;
|
||||
} else {
|
||||
if(rmtx != 0U) {
|
||||
if(xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) {
|
||||
if(timeout != 0U) {
|
||||
stat = FuriStatusErrorTimeout;
|
||||
} else {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(xSemaphoreTake(hMutex, timeout) != pdPASS) {
|
||||
if(timeout != 0U) {
|
||||
stat = FuriStatusErrorTimeout;
|
||||
} else {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
|
||||
} else if(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
|
||||
if(xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) {
|
||||
if(timeout != 0U) {
|
||||
stat = FuriStatusErrorTimeout;
|
||||
} else {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
}
|
||||
|
||||
} else if(mutex_type == queueQUEUE_TYPE_MUTEX) {
|
||||
if(xSemaphoreTake(hMutex, timeout) != pdPASS) {
|
||||
if(timeout != 0U) {
|
||||
stat = FuriStatusErrorTimeout;
|
||||
} else {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
|
||||
/* Return execution status */
|
||||
return (stat);
|
||||
return stat;
|
||||
}
|
||||
|
||||
FuriStatus furi_mutex_release(FuriMutex* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
SemaphoreHandle_t hMutex;
|
||||
FuriStatus stat;
|
||||
uint32_t rmtx;
|
||||
SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance);
|
||||
const uint8_t mutex_type = instance->container.ucQueueType;
|
||||
|
||||
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
|
||||
|
||||
/* Extract recursive mutex flag */
|
||||
rmtx = (uint32_t)instance & 1U;
|
||||
|
||||
stat = FuriStatusOk;
|
||||
FuriStatus stat = FuriStatusOk;
|
||||
|
||||
if(FURI_IS_IRQ_MODE()) {
|
||||
stat = FuriStatusErrorISR;
|
||||
} else if(hMutex == NULL) {
|
||||
stat = FuriStatusErrorParameter;
|
||||
} else {
|
||||
if(rmtx != 0U) {
|
||||
if(xSemaphoreGiveRecursive(hMutex) != pdPASS) {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
} else {
|
||||
if(xSemaphoreGive(hMutex) != pdPASS) {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
|
||||
} else if(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
|
||||
if(xSemaphoreGiveRecursive(hMutex) != pdPASS) {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
|
||||
} else if(mutex_type == queueQUEUE_TYPE_MUTEX) {
|
||||
if(xSemaphoreGive(hMutex) != pdPASS) {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
|
||||
/* Return execution status */
|
||||
return (stat);
|
||||
return stat;
|
||||
}
|
||||
|
||||
FuriThreadId furi_mutex_get_owner(FuriMutex* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
SemaphoreHandle_t hMutex;
|
||||
SemaphoreHandle_t hMutex = (SemaphoreHandle_t)instance;
|
||||
|
||||
FuriThreadId owner;
|
||||
|
||||
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U);
|
||||
|
||||
if(hMutex == NULL) {
|
||||
owner = 0;
|
||||
} else if(FURI_IS_IRQ_MODE()) {
|
||||
if(FURI_IS_IRQ_MODE()) {
|
||||
owner = (FuriThreadId)xSemaphoreGetMutexHolderFromISR(hMutex);
|
||||
} else {
|
||||
owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex);
|
||||
}
|
||||
|
||||
/* Return owner thread ID */
|
||||
return (owner);
|
||||
return owner;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ typedef enum {
|
||||
FuriMutexTypeRecursive,
|
||||
} FuriMutexType;
|
||||
|
||||
typedef void FuriMutex;
|
||||
typedef struct FuriMutex FuriMutex;
|
||||
|
||||
/** Allocate FuriMutex
|
||||
*
|
||||
|
||||
@@ -5,36 +5,43 @@
|
||||
#include <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
|
||||
struct FuriSemaphore {
|
||||
StaticSemaphore_t container;
|
||||
};
|
||||
|
||||
// IMPORTANT: container MUST be the FIRST struct member
|
||||
static_assert(offsetof(FuriSemaphore, container) == 0);
|
||||
|
||||
FuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count) {
|
||||
furi_check(!FURI_IS_IRQ_MODE());
|
||||
furi_check((max_count > 0U) && (initial_count <= max_count));
|
||||
|
||||
SemaphoreHandle_t hSemaphore = NULL;
|
||||
FuriSemaphore* instance = malloc(sizeof(FuriSemaphore));
|
||||
|
||||
SemaphoreHandle_t hSemaphore;
|
||||
|
||||
if(max_count == 1U) {
|
||||
hSemaphore = xSemaphoreCreateBinary();
|
||||
if((hSemaphore != NULL) && (initial_count != 0U)) {
|
||||
if(xSemaphoreGive(hSemaphore) != pdPASS) {
|
||||
vSemaphoreDelete(hSemaphore);
|
||||
hSemaphore = NULL;
|
||||
}
|
||||
}
|
||||
hSemaphore = xSemaphoreCreateBinaryStatic(&instance->container);
|
||||
} else {
|
||||
hSemaphore = xSemaphoreCreateCounting(max_count, initial_count);
|
||||
hSemaphore =
|
||||
xSemaphoreCreateCountingStatic(max_count, initial_count, &instance->container);
|
||||
}
|
||||
|
||||
furi_check(hSemaphore);
|
||||
furi_check(hSemaphore == (SemaphoreHandle_t)instance);
|
||||
|
||||
/* Return semaphore ID */
|
||||
return ((FuriSemaphore*)hSemaphore);
|
||||
if(max_count == 1U && initial_count != 0U) {
|
||||
furi_check(xSemaphoreGive(hSemaphore) == pdPASS);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void furi_semaphore_free(FuriSemaphore* instance) {
|
||||
furi_check(instance);
|
||||
furi_check(!FURI_IS_IRQ_MODE());
|
||||
|
||||
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance;
|
||||
|
||||
vSemaphoreDelete(hSemaphore);
|
||||
vSemaphoreDelete((SemaphoreHandle_t)instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) {
|
||||
@@ -58,6 +65,7 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if(xSemaphoreTake(hSemaphore, (TickType_t)timeout) != pdPASS) {
|
||||
if(timeout != 0U) {
|
||||
@@ -68,8 +76,7 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Return execution status */
|
||||
return (stat);
|
||||
return stat;
|
||||
}
|
||||
|
||||
FuriStatus furi_semaphore_release(FuriSemaphore* instance) {
|
||||
@@ -89,14 +96,14 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) {
|
||||
} else {
|
||||
portYIELD_FROM_ISR(yield);
|
||||
}
|
||||
|
||||
} else {
|
||||
if(xSemaphoreGive(hSemaphore) != pdPASS) {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return execution status */
|
||||
return (stat);
|
||||
return stat;
|
||||
}
|
||||
|
||||
uint32_t furi_semaphore_get_count(FuriSemaphore* instance) {
|
||||
@@ -111,6 +118,5 @@ uint32_t furi_semaphore_get_count(FuriSemaphore* instance) {
|
||||
count = (uint32_t)uxSemaphoreGetCount(hSemaphore);
|
||||
}
|
||||
|
||||
/* Return number of tokens */
|
||||
return (count);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void FuriSemaphore;
|
||||
typedef struct FuriSemaphore FuriSemaphore;
|
||||
|
||||
/** Allocate semaphore
|
||||
*
|
||||
|
||||
@@ -6,24 +6,42 @@
|
||||
#include <FreeRTOS.h>
|
||||
#include <FreeRTOS-Kernel/include/stream_buffer.h>
|
||||
|
||||
struct FuriStreamBuffer {
|
||||
StaticStreamBuffer_t container;
|
||||
uint8_t buffer[];
|
||||
};
|
||||
|
||||
// IMPORTANT: container MUST be the FIRST struct member
|
||||
static_assert(offsetof(FuriStreamBuffer, container) == 0);
|
||||
// IMPORTANT: buffer MUST be the LAST struct member
|
||||
static_assert(offsetof(FuriStreamBuffer, buffer) == sizeof(FuriStreamBuffer));
|
||||
|
||||
FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level) {
|
||||
furi_check(size != 0);
|
||||
|
||||
StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level);
|
||||
furi_check(handle);
|
||||
// Actual FreeRTOS usable buffer size seems to be one less
|
||||
const size_t buffer_size = size + 1;
|
||||
|
||||
return handle;
|
||||
FuriStreamBuffer* stream_buffer = malloc(sizeof(FuriStreamBuffer) + buffer_size);
|
||||
StreamBufferHandle_t hStreamBuffer = xStreamBufferCreateStatic(
|
||||
buffer_size, trigger_level, stream_buffer->buffer, &stream_buffer->container);
|
||||
|
||||
furi_check(hStreamBuffer == (StreamBufferHandle_t)stream_buffer);
|
||||
|
||||
return stream_buffer;
|
||||
};
|
||||
|
||||
void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer) {
|
||||
furi_check(stream_buffer);
|
||||
|
||||
vStreamBufferDelete(stream_buffer);
|
||||
vStreamBufferDelete((StreamBufferHandle_t)stream_buffer);
|
||||
free(stream_buffer);
|
||||
};
|
||||
|
||||
bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level) {
|
||||
furi_check(stream_buffer);
|
||||
return xStreamBufferSetTriggerLevel(stream_buffer, trigger_level) == pdTRUE;
|
||||
return xStreamBufferSetTriggerLevel((StreamBufferHandle_t)stream_buffer, trigger_level) ==
|
||||
pdTRUE;
|
||||
};
|
||||
|
||||
size_t furi_stream_buffer_send(
|
||||
@@ -37,10 +55,10 @@ size_t furi_stream_buffer_send(
|
||||
|
||||
if(FURI_IS_IRQ_MODE()) {
|
||||
BaseType_t yield;
|
||||
ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield);
|
||||
ret = xStreamBufferSendFromISR((StreamBufferHandle_t)stream_buffer, data, length, &yield);
|
||||
portYIELD_FROM_ISR(yield);
|
||||
} else {
|
||||
ret = xStreamBufferSend(stream_buffer, data, length, timeout);
|
||||
ret = xStreamBufferSend((StreamBufferHandle_t)stream_buffer, data, length, timeout);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -57,10 +75,11 @@ size_t furi_stream_buffer_receive(
|
||||
|
||||
if(FURI_IS_IRQ_MODE()) {
|
||||
BaseType_t yield;
|
||||
ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield);
|
||||
ret =
|
||||
xStreamBufferReceiveFromISR((StreamBufferHandle_t)stream_buffer, data, length, &yield);
|
||||
portYIELD_FROM_ISR(yield);
|
||||
} else {
|
||||
ret = xStreamBufferReceive(stream_buffer, data, length, timeout);
|
||||
ret = xStreamBufferReceive((StreamBufferHandle_t)stream_buffer, data, length, timeout);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -69,33 +88,33 @@ size_t furi_stream_buffer_receive(
|
||||
size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer) {
|
||||
furi_check(stream_buffer);
|
||||
|
||||
return xStreamBufferBytesAvailable(stream_buffer);
|
||||
return xStreamBufferBytesAvailable((StreamBufferHandle_t)stream_buffer);
|
||||
};
|
||||
|
||||
size_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer) {
|
||||
furi_check(stream_buffer);
|
||||
|
||||
return xStreamBufferSpacesAvailable(stream_buffer);
|
||||
return xStreamBufferSpacesAvailable((StreamBufferHandle_t)stream_buffer);
|
||||
};
|
||||
|
||||
bool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer) {
|
||||
furi_check(stream_buffer);
|
||||
|
||||
return xStreamBufferIsFull(stream_buffer) == pdTRUE;
|
||||
return xStreamBufferIsFull((StreamBufferHandle_t)stream_buffer) == pdTRUE;
|
||||
};
|
||||
|
||||
bool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer) {
|
||||
furi_check(stream_buffer);
|
||||
|
||||
return (xStreamBufferIsEmpty(stream_buffer) == pdTRUE);
|
||||
return (xStreamBufferIsEmpty((StreamBufferHandle_t)stream_buffer) == pdTRUE);
|
||||
};
|
||||
|
||||
FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer) {
|
||||
furi_check(stream_buffer);
|
||||
|
||||
if(xStreamBufferReset(stream_buffer) == pdPASS) {
|
||||
if(xStreamBufferReset((StreamBufferHandle_t)stream_buffer) == pdPASS) {
|
||||
return FuriStatusOk;
|
||||
} else {
|
||||
return FuriStatusError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void FuriStreamBuffer;
|
||||
typedef struct FuriStreamBuffer FuriStreamBuffer;
|
||||
|
||||
/**
|
||||
* @brief Allocate stream buffer instance.
|
||||
|
||||
@@ -27,6 +27,10 @@ struct FuriThreadStdout {
|
||||
};
|
||||
|
||||
struct FuriThread {
|
||||
StaticTask_t container;
|
||||
TaskHandle_t task_handle;
|
||||
StackType_t* stack_buffer;
|
||||
|
||||
FuriThreadState state;
|
||||
int32_t ret;
|
||||
|
||||
@@ -41,7 +45,7 @@ struct FuriThread {
|
||||
|
||||
FuriThreadPriority priority;
|
||||
|
||||
TaskHandle_t task_handle;
|
||||
size_t stack_size;
|
||||
size_t heap_size;
|
||||
|
||||
FuriThreadStdout output;
|
||||
@@ -50,10 +54,11 @@ struct FuriThread {
|
||||
// this ensures that the size of this structure is minimal
|
||||
bool is_service;
|
||||
bool heap_trace_enabled;
|
||||
|
||||
size_t stack_size;
|
||||
};
|
||||
|
||||
// IMPORTANT: container MUST be the FIRST struct member
|
||||
static_assert(offsetof(FuriThread, container) == 0);
|
||||
|
||||
static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size);
|
||||
static int32_t __furi_thread_stdout_flush(FuriThread* thread);
|
||||
|
||||
@@ -92,6 +97,8 @@ static void furi_thread_body(void* context) {
|
||||
|
||||
thread->ret = thread->callback(thread->context);
|
||||
|
||||
furi_check(!thread->is_service, "Service threads MUST NOT return");
|
||||
|
||||
if(thread->heap_trace_enabled == true) {
|
||||
furi_delay_ms(33);
|
||||
thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle);
|
||||
@@ -106,13 +113,6 @@ static void furi_thread_body(void* context) {
|
||||
|
||||
furi_check(thread->state == FuriThreadStateRunning);
|
||||
|
||||
if(thread->is_service) {
|
||||
FURI_LOG_W(
|
||||
TAG,
|
||||
"%s service thread TCB memory will not be reclaimed",
|
||||
thread->name ? thread->name : "<unknown service>");
|
||||
}
|
||||
|
||||
// flush stdout
|
||||
__furi_thread_stdout_flush(thread);
|
||||
|
||||
@@ -122,10 +122,8 @@ static void furi_thread_body(void* context) {
|
||||
furi_thread_catch();
|
||||
}
|
||||
|
||||
FuriThread* furi_thread_alloc(void) {
|
||||
FuriThread* thread = malloc(sizeof(FuriThread));
|
||||
static void furi_thread_init_common(FuriThread* thread) {
|
||||
thread->output.buffer = furi_string_alloc();
|
||||
thread->is_service = false;
|
||||
|
||||
FuriThread* parent = NULL;
|
||||
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
|
||||
@@ -150,6 +148,32 @@ FuriThread* furi_thread_alloc(void) {
|
||||
} else {
|
||||
thread->heap_trace_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
FuriThread* furi_thread_alloc(void) {
|
||||
FuriThread* thread = malloc(sizeof(FuriThread));
|
||||
|
||||
furi_thread_init_common(thread);
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
FuriThread* furi_thread_alloc_service(
|
||||
const char* name,
|
||||
uint32_t stack_size,
|
||||
FuriThreadCallback callback,
|
||||
void* context) {
|
||||
FuriThread* thread = memmgr_alloc_from_pool(sizeof(FuriThread));
|
||||
|
||||
furi_thread_init_common(thread);
|
||||
|
||||
thread->stack_buffer = memmgr_alloc_from_pool(stack_size);
|
||||
thread->stack_size = stack_size;
|
||||
thread->is_service = true;
|
||||
|
||||
furi_thread_set_name(thread, name);
|
||||
furi_thread_set_callback(thread, callback);
|
||||
furi_thread_set_context(thread, context);
|
||||
|
||||
return thread;
|
||||
}
|
||||
@@ -169,15 +193,20 @@ FuriThread* furi_thread_alloc_ex(
|
||||
|
||||
void furi_thread_free(FuriThread* thread) {
|
||||
furi_check(thread);
|
||||
|
||||
// Ensure that use join before free
|
||||
// Cannot free a service thread
|
||||
furi_check(thread->is_service == false);
|
||||
// Cannot free a non-joined thread
|
||||
furi_check(thread->state == FuriThreadStateStopped);
|
||||
furi_check(thread->task_handle == NULL);
|
||||
|
||||
if(thread->name) free(thread->name);
|
||||
if(thread->appid) free(thread->appid);
|
||||
furi_string_free(thread->output.buffer);
|
||||
furi_thread_set_name(thread, NULL);
|
||||
furi_thread_set_appid(thread, NULL);
|
||||
|
||||
if(thread->stack_buffer) {
|
||||
free(thread->stack_buffer);
|
||||
}
|
||||
|
||||
furi_string_free(thread->output.buffer);
|
||||
free(thread);
|
||||
}
|
||||
|
||||
@@ -185,7 +214,9 @@ void furi_thread_set_name(FuriThread* thread, const char* name) {
|
||||
furi_check(thread);
|
||||
furi_check(thread->state == FuriThreadStateStopped);
|
||||
|
||||
if(thread->name) free(thread->name);
|
||||
if(thread->name) {
|
||||
free(thread->name);
|
||||
}
|
||||
|
||||
thread->name = name ? strdup(name) : NULL;
|
||||
}
|
||||
@@ -193,19 +224,28 @@ void furi_thread_set_name(FuriThread* thread, const char* name) {
|
||||
void furi_thread_set_appid(FuriThread* thread, const char* appid) {
|
||||
furi_check(thread);
|
||||
furi_check(thread->state == FuriThreadStateStopped);
|
||||
if(thread->appid) free(thread->appid);
|
||||
thread->appid = appid ? strdup(appid) : NULL;
|
||||
}
|
||||
|
||||
void furi_thread_mark_as_service(FuriThread* thread) {
|
||||
thread->is_service = true;
|
||||
if(thread->appid) {
|
||||
free(thread->appid);
|
||||
}
|
||||
|
||||
thread->appid = appid ? strdup(appid) : NULL;
|
||||
}
|
||||
|
||||
void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) {
|
||||
furi_check(thread);
|
||||
furi_check(thread->state == FuriThreadStateStopped);
|
||||
furi_check(stack_size % 4 == 0);
|
||||
furi_check(stack_size);
|
||||
furi_check(stack_size <= THREAD_MAX_STACK_SIZE);
|
||||
furi_check(stack_size % sizeof(StackType_t) == 0);
|
||||
// Stack size cannot be configured for a thread that has been marked as a service
|
||||
furi_check(thread->is_service == false);
|
||||
|
||||
if(thread->stack_buffer) {
|
||||
free(thread->stack_buffer);
|
||||
}
|
||||
|
||||
thread->stack_buffer = malloc(stack_size);
|
||||
thread->stack_size = stack_size;
|
||||
}
|
||||
|
||||
@@ -270,24 +310,19 @@ void furi_thread_start(FuriThread* thread) {
|
||||
|
||||
furi_thread_set_state(thread, FuriThreadStateStarting);
|
||||
|
||||
uint32_t stack = thread->stack_size / sizeof(StackType_t);
|
||||
uint32_t stack_depth = thread->stack_size / sizeof(StackType_t);
|
||||
UBaseType_t priority = thread->priority ? thread->priority : FuriThreadPriorityNormal;
|
||||
if(thread->is_service) {
|
||||
thread->task_handle = xTaskCreateStatic(
|
||||
furi_thread_body,
|
||||
thread->name,
|
||||
stack,
|
||||
thread,
|
||||
priority,
|
||||
memmgr_alloc_from_pool(sizeof(StackType_t) * stack),
|
||||
memmgr_alloc_from_pool(sizeof(StaticTask_t)));
|
||||
} else {
|
||||
BaseType_t ret = xTaskCreate(
|
||||
furi_thread_body, thread->name, stack, thread, priority, &thread->task_handle);
|
||||
furi_check(ret == pdPASS);
|
||||
}
|
||||
|
||||
furi_check(thread->task_handle);
|
||||
thread->task_handle = xTaskCreateStatic(
|
||||
furi_thread_body,
|
||||
thread->name,
|
||||
stack_depth,
|
||||
thread,
|
||||
priority,
|
||||
thread->stack_buffer,
|
||||
&thread->container);
|
||||
|
||||
furi_check(thread->task_handle == (TaskHandle_t)&thread->container);
|
||||
}
|
||||
|
||||
void furi_thread_cleanup_tcb_event(TaskHandle_t task) {
|
||||
@@ -302,7 +337,9 @@ void furi_thread_cleanup_tcb_event(TaskHandle_t task) {
|
||||
|
||||
bool furi_thread_join(FuriThread* thread) {
|
||||
furi_check(thread);
|
||||
|
||||
// Cannot join a service thread
|
||||
furi_check(!thread->is_service);
|
||||
// Cannot join a thread to itself
|
||||
furi_check(furi_thread_get_current() != thread);
|
||||
|
||||
// !!! IMPORTANT NOTICE !!!
|
||||
@@ -390,7 +427,7 @@ uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags) {
|
||||
}
|
||||
}
|
||||
/* Return flags after setting */
|
||||
return (rflags);
|
||||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t furi_thread_flags_clear(uint32_t flags) {
|
||||
@@ -419,7 +456,7 @@ uint32_t furi_thread_flags_clear(uint32_t flags) {
|
||||
}
|
||||
|
||||
/* Return flags before clearing */
|
||||
return (rflags);
|
||||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t furi_thread_flags_get(void) {
|
||||
@@ -437,7 +474,7 @@ uint32_t furi_thread_flags_get(void) {
|
||||
}
|
||||
}
|
||||
|
||||
return (rflags);
|
||||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
|
||||
@@ -507,7 +544,7 @@ uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeo
|
||||
}
|
||||
|
||||
/* Return flags before clearing */
|
||||
return (rflags);
|
||||
return rflags;
|
||||
}
|
||||
|
||||
uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count) {
|
||||
@@ -536,7 +573,7 @@ uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_c
|
||||
vPortFree(task);
|
||||
}
|
||||
|
||||
return (count);
|
||||
return count;
|
||||
}
|
||||
|
||||
const char* furi_thread_get_name(FuriThreadId thread_id) {
|
||||
@@ -549,7 +586,7 @@ const char* furi_thread_get_name(FuriThreadId thread_id) {
|
||||
name = pcTaskGetName(hTask);
|
||||
}
|
||||
|
||||
return (name);
|
||||
return name;
|
||||
}
|
||||
|
||||
const char* furi_thread_get_appid(FuriThreadId thread_id) {
|
||||
@@ -563,7 +600,7 @@ const char* furi_thread_get_appid(FuriThreadId thread_id) {
|
||||
}
|
||||
}
|
||||
|
||||
return (appid);
|
||||
return appid;
|
||||
}
|
||||
|
||||
uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) {
|
||||
@@ -576,7 +613,7 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) {
|
||||
sz = (uint32_t)(uxTaskGetStackHighWaterMark(hTask) * sizeof(StackType_t));
|
||||
}
|
||||
|
||||
return (sz);
|
||||
return sz;
|
||||
}
|
||||
|
||||
static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file thread.h
|
||||
* Furi: Furi Thread API
|
||||
* @brief Furi: Furi Thread API
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -15,14 +15,20 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** FuriThreadState */
|
||||
/**
|
||||
* @brief Enumeration of possible FuriThread states.
|
||||
*
|
||||
* Many of the FuriThread functions MUST ONLY be called when the thread is STOPPED.
|
||||
*/
|
||||
typedef enum {
|
||||
FuriThreadStateStopped,
|
||||
FuriThreadStateStarting,
|
||||
FuriThreadStateRunning,
|
||||
FuriThreadStateStopped, /**< Thread is stopped */
|
||||
FuriThreadStateStarting, /**< Thread is starting */
|
||||
FuriThreadStateRunning, /**< Thread is running */
|
||||
} FuriThreadState;
|
||||
|
||||
/** FuriThreadPriority */
|
||||
/**
|
||||
* @brief Enumeration of possible FuriThread priorities.
|
||||
*/
|
||||
typedef enum {
|
||||
FuriThreadPriorityNone = 0, /**< Uninitialized, choose system default */
|
||||
FuriThreadPriorityIdle = 1, /**< Idle priority */
|
||||
@@ -35,42 +41,85 @@ typedef enum {
|
||||
(FURI_CONFIG_THREAD_MAX_PRIORITIES - 1), /**< Deferred ISR (highest possible) */
|
||||
} FuriThreadPriority;
|
||||
|
||||
/** FuriThread anonymous structure */
|
||||
/**
|
||||
* @brief FuriThread opaque type.
|
||||
*/
|
||||
typedef struct FuriThread FuriThread;
|
||||
|
||||
/** FuriThreadId proxy type to OS low level functions */
|
||||
/**
|
||||
* @brief Unique thread identifier type (used by the OS kernel).
|
||||
*/
|
||||
typedef void* FuriThreadId;
|
||||
|
||||
/** FuriThreadCallback Your callback to run in new thread
|
||||
* @warning never use osThreadExit in FuriThread
|
||||
/**
|
||||
* @brief Thread callback function pointer type.
|
||||
*
|
||||
* The function to be used as a thread callback MUST follow this signature.
|
||||
*
|
||||
* @param[in,out] context pointer to a user-specified object
|
||||
* @return value to be used as the thread return code
|
||||
*/
|
||||
typedef int32_t (*FuriThreadCallback)(void* context);
|
||||
|
||||
/** Write to stdout callback
|
||||
* @param data pointer to data
|
||||
* @param size data size @warning your handler must consume everything
|
||||
/**
|
||||
* @brief Standard output callback function pointer type.
|
||||
*
|
||||
* The function to be used as a standard output callback MUST follow this signature.
|
||||
*
|
||||
* @warning The handler MUST process ALL of the provided data before returning.
|
||||
*
|
||||
* @param[in] data pointer to the data to be written to the standard out
|
||||
* @param[in] size size of the data in bytes
|
||||
*/
|
||||
typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size);
|
||||
|
||||
/** FuriThread state change callback called upon thread state change
|
||||
* @param state new thread state
|
||||
* @param context callback context
|
||||
/**
|
||||
* @brief State change callback function pointer type.
|
||||
*
|
||||
* The function to be used as a state callback MUST follow this signature.
|
||||
*
|
||||
* @param[in] state identifier of the state the thread has transitioned to
|
||||
* @param[in,out] context pointer to a user-specified object
|
||||
*/
|
||||
typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context);
|
||||
|
||||
/** Allocate FuriThread
|
||||
/**
|
||||
* @brief Create a FuriThread instance.
|
||||
*
|
||||
* @return FuriThread instance
|
||||
* @return pointer to the created FuriThread instance
|
||||
*/
|
||||
FuriThread* furi_thread_alloc(void);
|
||||
|
||||
/** Allocate FuriThread, shortcut version
|
||||
/**
|
||||
* @brief Create a FuriThread instance (service mode).
|
||||
*
|
||||
* Service threads are more memory efficient, but have
|
||||
* the following limitations:
|
||||
*
|
||||
* - Cannot return from the callback
|
||||
* - Cannot be joined or freed
|
||||
* - Stack size cannot be altered
|
||||
*
|
||||
* @param[in] name human-readable thread name (can be NULL)
|
||||
* @param[in] stack_size stack size in bytes (cannot be changed later)
|
||||
* @param[in] callback pointer to a function to be executed in this thread
|
||||
* @param[in] context pointer to a user-specified object (will be passed to the callback)
|
||||
* @return pointer to the created FuriThread instance
|
||||
*/
|
||||
FuriThread* furi_thread_alloc_service(
|
||||
const char* name,
|
||||
uint32_t stack_size,
|
||||
FuriThreadCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* @brief Create a FuriThread instance w/ extra parameters.
|
||||
*
|
||||
* @param name
|
||||
* @param stack_size
|
||||
* @param callback
|
||||
* @param context
|
||||
* @return FuriThread*
|
||||
* @param[in] name human-readable thread name (can be NULL)
|
||||
* @param[in] stack_size stack size in bytes (can be changed later)
|
||||
* @param[in] callback pointer to a function to be executed in this thread
|
||||
* @param[in] context pointer to a user-specified object (will be passed to the callback)
|
||||
* @return pointer to the created FuriThread instance
|
||||
*/
|
||||
FuriThread* furi_thread_alloc_ex(
|
||||
const char* name,
|
||||
@@ -78,261 +127,339 @@ FuriThread* furi_thread_alloc_ex(
|
||||
FuriThreadCallback callback,
|
||||
void* context);
|
||||
|
||||
/** Release FuriThread
|
||||
/**
|
||||
* @brief Delete a FuriThread instance.
|
||||
*
|
||||
* @warning see furi_thread_join
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* @warning see furi_thread_join for caveats on stopping a thread.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be deleted
|
||||
*/
|
||||
void furi_thread_free(FuriThread* thread);
|
||||
|
||||
/** Set FuriThread name
|
||||
/**
|
||||
* @brief Set the name of a FuriThread instance.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* @param name string
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
* @param[in] name human-readable thread name (can be NULL)
|
||||
*/
|
||||
void furi_thread_set_name(FuriThread* thread, const char* name);
|
||||
|
||||
/**
|
||||
* @brief Set FuriThread appid
|
||||
* @brief Set the application ID of a FuriThread instance.
|
||||
*
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* Technically, it is like a "process id", but it is not a system-wide unique identifier.
|
||||
* All threads spawned by the same app will have the same appid.
|
||||
*
|
||||
* @param thread
|
||||
* @param appid
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
* @param[in] appid thread application ID (can be NULL)
|
||||
*/
|
||||
void furi_thread_set_appid(FuriThread* thread, const char* appid);
|
||||
|
||||
/** Mark thread as service
|
||||
* The service cannot be stopped or removed, and cannot exit from the thread body
|
||||
*
|
||||
* @param thread
|
||||
*/
|
||||
void furi_thread_mark_as_service(FuriThread* thread);
|
||||
|
||||
/** Set FuriThread stack size
|
||||
/**
|
||||
* @brief Set the stack size of a FuriThread instance.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* @param stack_size stack size in bytes
|
||||
* The thread MUST be stopped when calling this function. Additionally, it is NOT possible
|
||||
* to change the stack size of a service thread under any circumstances.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
* @param[in] stack_size stack size in bytes
|
||||
*/
|
||||
void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size);
|
||||
|
||||
/** Set FuriThread callback
|
||||
/**
|
||||
* @brief Set the user callback function to be executed in a FuriThread.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* @param callback FuriThreadCallback, called upon thread run
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
* @param[in] callback pointer to a user-specified function to be executed in this thread
|
||||
*/
|
||||
void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback);
|
||||
|
||||
/** Set FuriThread context
|
||||
/**
|
||||
* @brief Set the callback function context.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* @param context pointer to context for thread callback
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
* @param[in] context pointer to a user-specified object (will be passed to the callback, can be NULL)
|
||||
*/
|
||||
void furi_thread_set_context(FuriThread* thread, void* context);
|
||||
|
||||
/** Set FuriThread priority
|
||||
/**
|
||||
* @brief Set the priority of a FuriThread.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* @param priority FuriThreadPriority value
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
* @param[in] priority priority level value
|
||||
*/
|
||||
void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority);
|
||||
|
||||
/** Get FuriThread priority
|
||||
/**
|
||||
* @brief Get the priority of a FuriThread.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* @return FuriThreadPriority value
|
||||
* @param[in] thread pointer to the FuriThread instance to be queried
|
||||
* @return priority level value
|
||||
*/
|
||||
FuriThreadPriority furi_thread_get_priority(FuriThread* thread);
|
||||
|
||||
/** Set current thread priority
|
||||
/**
|
||||
* @brief Set the priority of the current FuriThread.
|
||||
*
|
||||
* @param priority FuriThreadPriority value
|
||||
* @param priority priority level value
|
||||
*/
|
||||
void furi_thread_set_current_priority(FuriThreadPriority priority);
|
||||
|
||||
/** Get current thread priority
|
||||
/**
|
||||
* @brief Get the priority of the current FuriThread.
|
||||
*
|
||||
* @return FuriThreadPriority value
|
||||
* @return priority level value
|
||||
*/
|
||||
FuriThreadPriority furi_thread_get_current_priority(void);
|
||||
|
||||
/** Set FuriThread state change callback
|
||||
/**
|
||||
* Set the callback function to be executed upon a state thransition of a FuriThread.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* @param callback state change callback
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
* @param[in] callback pointer to a user-specified callback function
|
||||
*/
|
||||
void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback);
|
||||
|
||||
/** Set FuriThread state change context
|
||||
/**
|
||||
* @brief Set the state change callback context.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* @param context pointer to context
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
* @param[in] context pointer to a user-specified object (will be passed to the callback, can be NULL)
|
||||
*/
|
||||
void furi_thread_set_state_context(FuriThread* thread, void* context);
|
||||
|
||||
/** Get FuriThread state
|
||||
/**
|
||||
* @brief Get the state of a FuriThread isntance.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
*
|
||||
* @return thread state from FuriThreadState
|
||||
* @param[in] thread pointer to the FuriThread instance to be queried
|
||||
* @return thread state value
|
||||
*/
|
||||
FuriThreadState furi_thread_get_state(FuriThread* thread);
|
||||
|
||||
/** Start FuriThread
|
||||
/**
|
||||
* @brief Start a FuriThread instance.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be started
|
||||
*/
|
||||
void furi_thread_start(FuriThread* thread);
|
||||
|
||||
/** Join FuriThread
|
||||
/**
|
||||
* @brief Wait for a FuriThread to exit.
|
||||
*
|
||||
* @warning Use this method only when CPU is not busy(Idle task receives
|
||||
* control), otherwise it will wait forever.
|
||||
* The thread callback function must return in order for the FuriThread instance to become joinable.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* @warning Use this method only when the CPU is not busy (i.e. when the
|
||||
* Idle task receives control), otherwise it will wait forever.
|
||||
*
|
||||
* @return bool
|
||||
* @param[in] thread pointer to the FuriThread instance to be joined
|
||||
* @return always true
|
||||
*/
|
||||
bool furi_thread_join(FuriThread* thread);
|
||||
|
||||
/** Get FreeRTOS FuriThreadId for FuriThread instance
|
||||
/**
|
||||
* @brief Get the unique identifier of a FuriThread instance.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
*
|
||||
* @return FuriThreadId or NULL
|
||||
* @param[in] thread pointer to the FuriThread instance to be queried
|
||||
* @return unique identifier value or NULL if thread is not running
|
||||
*/
|
||||
FuriThreadId furi_thread_get_id(FuriThread* thread);
|
||||
|
||||
/** Enable heap tracing
|
||||
/**
|
||||
* @brief Enable heap usage tracing for a FuriThread.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
*/
|
||||
void furi_thread_enable_heap_trace(FuriThread* thread);
|
||||
|
||||
/** Disable heap tracing
|
||||
/**
|
||||
* @brief Disable heap usage tracing for a FuriThread.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param[in,out] thread pointer to the FuriThread instance to be modified
|
||||
*/
|
||||
void furi_thread_disable_heap_trace(FuriThread* thread);
|
||||
|
||||
/** Get thread heap size
|
||||
/**
|
||||
* @brief Get heap usage by a FuriThread instance.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* The heap trace MUST be enabled before callgin this function.
|
||||
*
|
||||
* @return size in bytes
|
||||
* @param[in] thread pointer to the FuriThread instance to be queried
|
||||
* @return heap usage in bytes
|
||||
*/
|
||||
size_t furi_thread_get_heap_size(FuriThread* thread);
|
||||
|
||||
/** Get thread return code
|
||||
/**
|
||||
* @brief Get the return code of a FuriThread instance.
|
||||
*
|
||||
* @param thread FuriThread instance
|
||||
* This value is equal to the return value of the thread callback function.
|
||||
*
|
||||
* @return return code
|
||||
* The thread MUST be stopped when calling this function.
|
||||
*
|
||||
* @param[in] thread pointer to the FuriThread instance to be queried
|
||||
* @return return code value
|
||||
*/
|
||||
int32_t furi_thread_get_return_code(FuriThread* thread);
|
||||
|
||||
/** Thread related methods that doesn't involve FuriThread directly */
|
||||
|
||||
/** Get FreeRTOS FuriThreadId for current thread
|
||||
/**
|
||||
* @brief Get the unique identifier of the current FuriThread.
|
||||
*
|
||||
* @return FuriThreadId or NULL
|
||||
* @return unique identifier value
|
||||
*/
|
||||
FuriThreadId furi_thread_get_current_id(void);
|
||||
|
||||
/** Get FuriThread instance for current thread
|
||||
/**
|
||||
* @brief Get the FuriThread instance associated with the current thread.
|
||||
*
|
||||
* @return pointer to FuriThread or NULL if this thread doesn't belongs to Furi
|
||||
* @return pointer to a FuriThread instance or NULL if this thread does not belong to Furi
|
||||
*/
|
||||
FuriThread* furi_thread_get_current(void);
|
||||
|
||||
/** Return control to scheduler */
|
||||
/**
|
||||
* @brief Return control to the scheduler.
|
||||
*/
|
||||
void furi_thread_yield(void);
|
||||
|
||||
/**
|
||||
* @brief Set the thread flags of a FuriThread.
|
||||
*
|
||||
* Can be used as a simple inter-thread communication mechanism.
|
||||
*
|
||||
* @param[in] thread_id unique identifier of the thread to be notified
|
||||
* @param[in] flags bitmask of thread flags to set
|
||||
* @return bitmask combination of previous and newly set flags
|
||||
*/
|
||||
uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags);
|
||||
|
||||
/**
|
||||
* @brief Clear the thread flags of the current FuriThread.
|
||||
*
|
||||
* @param[in] flags bitmask of thread flags to clear
|
||||
* @return bitmask of thread flags before clearing
|
||||
*/
|
||||
uint32_t furi_thread_flags_clear(uint32_t flags);
|
||||
|
||||
/**
|
||||
* @brief Get the thread flags of the current FuriThread.
|
||||
* @return current bitmask of thread flags
|
||||
*/
|
||||
uint32_t furi_thread_flags_get(void);
|
||||
|
||||
/**
|
||||
* @brief Wait for some thread flags to be set.
|
||||
*
|
||||
* @see FuriFlag for option and error flags.
|
||||
*
|
||||
* @param[in] flags bitmask of thread flags to wait for
|
||||
* @param[in] options combination of option flags determining the behavior of the function
|
||||
* @param[in] timeout maximum time to wait in milliseconds (use FuriWaitForever to wait forever)
|
||||
* @return bitmask combination of received thread and error flags
|
||||
*/
|
||||
uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Enumerate threads
|
||||
* @brief Enumerate all threads.
|
||||
*
|
||||
* @param thread_array array of FuriThreadId, where thread ids will be stored
|
||||
* @param array_item_count array size
|
||||
* @return uint32_t threads count
|
||||
* @param[out] thread_array pointer to the output array (must be properly allocated)
|
||||
* @param[in] array_item_count output array capacity in elements (NOT bytes)
|
||||
* @return total thread count (array_item_count or less)
|
||||
*/
|
||||
uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count);
|
||||
|
||||
/**
|
||||
* @brief Get thread name
|
||||
* @brief Get the name of a thread based on its unique identifier.
|
||||
*
|
||||
* @param thread_id
|
||||
* @return const char* name or NULL
|
||||
* @param[in] thread_id unique identifier of the thread to be queried
|
||||
* @return pointer to a zero-terminated string or NULL
|
||||
*/
|
||||
const char* furi_thread_get_name(FuriThreadId thread_id);
|
||||
|
||||
/**
|
||||
* @brief Get thread appid
|
||||
* @brief Get the application id of a thread based on its unique identifier.
|
||||
*
|
||||
* @param thread_id
|
||||
* @return const char* appid
|
||||
* @param[in] thread_id unique identifier of the thread to be queried
|
||||
* @return pointer to a zero-terminated string
|
||||
*/
|
||||
const char* furi_thread_get_appid(FuriThreadId thread_id);
|
||||
|
||||
/**
|
||||
* @brief Get thread stack watermark
|
||||
* @brief Get thread stack watermark.
|
||||
*
|
||||
* @param thread_id
|
||||
* @return uint32_t
|
||||
* @param[in] thread_id unique identifier of the thread to be queried
|
||||
* @return stack watermark value
|
||||
*/
|
||||
uint32_t furi_thread_get_stack_space(FuriThreadId thread_id);
|
||||
|
||||
/** Get STDOUT callback for thead
|
||||
/**
|
||||
* @brief Get the standard output callback for the current thead.
|
||||
*
|
||||
* @return STDOUT callback
|
||||
* @return pointer to the standard out callback function
|
||||
*/
|
||||
FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void);
|
||||
|
||||
/** Set STDOUT callback for thread
|
||||
/** Set standard output callback for the current thread.
|
||||
*
|
||||
* @param callback callback or NULL to clear
|
||||
* @param[in] callback pointer to the callback function or NULL to clear
|
||||
*/
|
||||
void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback);
|
||||
|
||||
/** Write data to buffered STDOUT
|
||||
/** Write data to buffered standard output.
|
||||
*
|
||||
* @param data input data
|
||||
* @param size input data size
|
||||
*
|
||||
* @return size_t written data size
|
||||
* @param[in] data pointer to the data to be written
|
||||
* @param[in] size data size in bytes
|
||||
* @return number of bytes that was actually written
|
||||
*/
|
||||
size_t furi_thread_stdout_write(const char* data, size_t size);
|
||||
|
||||
/** Flush data to STDOUT
|
||||
/**
|
||||
* @brief Flush buffered data to standard output.
|
||||
*
|
||||
* @return int32_t error code
|
||||
* @return error code value
|
||||
*/
|
||||
int32_t furi_thread_stdout_flush(void);
|
||||
|
||||
/** Suspend thread
|
||||
/**
|
||||
* @brief Suspend a thread.
|
||||
*
|
||||
* Suspended threads are no more receiving any of the processor time.
|
||||
*
|
||||
* @param thread_id thread id
|
||||
* @param[in] thread_id unique identifier of the thread to be suspended
|
||||
*/
|
||||
void furi_thread_suspend(FuriThreadId thread_id);
|
||||
|
||||
/** Resume thread
|
||||
/**
|
||||
* @brief Resume a thread.
|
||||
*
|
||||
* @param thread_id thread id
|
||||
* @param[in] thread_id unique identifier of the thread to be resumed
|
||||
*/
|
||||
void furi_thread_resume(FuriThreadId thread_id);
|
||||
|
||||
/** Get thread suspended state
|
||||
/**
|
||||
* @brief Test if a thread is suspended.
|
||||
*
|
||||
* @param thread_id thread id
|
||||
* @return true if thread is suspended
|
||||
* @param[in] thread_id unique identifier of the thread to be queried
|
||||
* @return true if thread is suspended, false otherwise
|
||||
*/
|
||||
bool furi_thread_is_suspended(FuriThreadId thread_id);
|
||||
|
||||
|
||||
@@ -5,56 +5,46 @@
|
||||
#include <FreeRTOS.h>
|
||||
#include <timers.h>
|
||||
|
||||
typedef struct {
|
||||
FuriTimerCallback func;
|
||||
void* context;
|
||||
} TimerCallback_t;
|
||||
struct FuriTimer {
|
||||
StaticTimer_t container;
|
||||
FuriTimerCallback cb_func;
|
||||
void* cb_context;
|
||||
volatile bool can_be_removed;
|
||||
};
|
||||
|
||||
// IMPORTANT: container MUST be the FIRST struct member
|
||||
static_assert(offsetof(FuriTimer, container) == 0);
|
||||
|
||||
static void TimerCallback(TimerHandle_t hTimer) {
|
||||
TimerCallback_t* callb;
|
||||
|
||||
/* Retrieve pointer to callback function and context */
|
||||
callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer);
|
||||
|
||||
/* Remove dynamic allocation flag */
|
||||
callb = (TimerCallback_t*)((uint32_t)callb & ~1U);
|
||||
|
||||
if(callb != NULL) {
|
||||
callb->func(callb->context);
|
||||
}
|
||||
FuriTimer* instance = pvTimerGetTimerID(hTimer);
|
||||
furi_check(instance);
|
||||
instance->cb_func(instance->cb_context);
|
||||
}
|
||||
|
||||
FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) {
|
||||
furi_check((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL));
|
||||
|
||||
TimerHandle_t hTimer;
|
||||
TimerCallback_t* callb;
|
||||
UBaseType_t reload;
|
||||
FuriTimer* instance = malloc(sizeof(FuriTimer));
|
||||
|
||||
hTimer = NULL;
|
||||
instance->cb_func = func;
|
||||
instance->cb_context = context;
|
||||
|
||||
/* Dynamic memory allocation is available: if memory for callback and */
|
||||
/* its context is not provided, allocate it from dynamic memory pool */
|
||||
callb = (TimerCallback_t*)malloc(sizeof(TimerCallback_t));
|
||||
const UBaseType_t reload = (type == FuriTimerTypeOnce ? pdFALSE : pdTRUE);
|
||||
const TimerHandle_t hTimer = xTimerCreateStatic(
|
||||
NULL, portMAX_DELAY, reload, instance, TimerCallback, &instance->container);
|
||||
|
||||
callb->func = func;
|
||||
callb->context = context;
|
||||
furi_check(hTimer == (TimerHandle_t)instance);
|
||||
|
||||
if(type == FuriTimerTypeOnce) {
|
||||
reload = pdFALSE;
|
||||
} else {
|
||||
reload = pdTRUE;
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/* Store callback memory dynamic allocation flag */
|
||||
callb = (TimerCallback_t*)((uint32_t)callb | 1U);
|
||||
// TimerCallback function is always provided as a callback and is used to call application
|
||||
// specified function with its context both stored in structure callb.
|
||||
hTimer = xTimerCreate(NULL, portMAX_DELAY, reload, callb, TimerCallback);
|
||||
furi_check(hTimer);
|
||||
static void furi_timer_epilogue(void* context, uint32_t arg) {
|
||||
furi_assert(context);
|
||||
UNUSED(arg);
|
||||
|
||||
/* Return timer ID */
|
||||
return ((FuriTimer*)hTimer);
|
||||
FuriTimer* instance = context;
|
||||
|
||||
instance->can_be_removed = true;
|
||||
}
|
||||
|
||||
void furi_timer_free(FuriTimer* instance) {
|
||||
@@ -62,26 +52,14 @@ void furi_timer_free(FuriTimer* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
TimerHandle_t hTimer = (TimerHandle_t)instance;
|
||||
TimerCallback_t* callb;
|
||||
furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS);
|
||||
furi_check(xTimerPendFunctionCall(furi_timer_epilogue, instance, 0, portMAX_DELAY) == pdPASS);
|
||||
|
||||
callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer);
|
||||
|
||||
if((uint32_t)callb & 1U) {
|
||||
/* If callback memory was allocated, it is only safe to free it with
|
||||
* the timer inactive. Send a stop command and wait for the timer to
|
||||
* be in an inactive state.
|
||||
*/
|
||||
furi_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS);
|
||||
while(furi_timer_is_running(instance)) furi_delay_tick(2);
|
||||
|
||||
/* Callback memory was allocated from dynamic pool, clear flag */
|
||||
callb = (TimerCallback_t*)((uint32_t)callb & ~1U);
|
||||
|
||||
/* Return allocated memory to dynamic pool */
|
||||
free(callb);
|
||||
while(!instance->can_be_removed) {
|
||||
furi_delay_tick(2);
|
||||
}
|
||||
|
||||
furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {
|
||||
@@ -98,8 +76,7 @@ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
|
||||
/* Return execution status */
|
||||
return (stat);
|
||||
return stat;
|
||||
}
|
||||
|
||||
FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) {
|
||||
@@ -117,8 +94,7 @@ FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) {
|
||||
stat = FuriStatusErrorResource;
|
||||
}
|
||||
|
||||
/* Return execution status */
|
||||
return (stat);
|
||||
return stat;
|
||||
}
|
||||
|
||||
FuriStatus furi_timer_stop(FuriTimer* instance) {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @file timer.h
|
||||
* @brief Furi software Timer API.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/base.h"
|
||||
@@ -13,7 +17,7 @@ typedef enum {
|
||||
FuriTimerTypePeriodic = 1 ///< Repeating timer.
|
||||
} FuriTimerType;
|
||||
|
||||
typedef void FuriTimer;
|
||||
typedef struct FuriTimer FuriTimer;
|
||||
|
||||
/** Allocate timer
|
||||
*
|
||||
|
||||
@@ -37,12 +37,11 @@ void flipper_init(void) {
|
||||
for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) {
|
||||
FURI_LOG_D(TAG, "Starting service %s", FLIPPER_SERVICES[i].name);
|
||||
|
||||
FuriThread* thread = furi_thread_alloc_ex(
|
||||
FuriThread* thread = furi_thread_alloc_service(
|
||||
FLIPPER_SERVICES[i].name,
|
||||
FLIPPER_SERVICES[i].stack_size,
|
||||
FLIPPER_SERVICES[i].app,
|
||||
NULL);
|
||||
furi_thread_mark_as_service(thread);
|
||||
furi_thread_set_appid(thread, FLIPPER_SERVICES[i].appid);
|
||||
|
||||
furi_thread_start(thread);
|
||||
@@ -67,4 +66,4 @@ void vApplicationGetTimerTaskMemory(
|
||||
*tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t));
|
||||
*stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH);
|
||||
*stack_size = configTIMER_TASK_STACK_DEPTH;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user