1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 04:34:43 +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:
Georgii Surkov
2024-06-05 20:04:03 +03:00
committed by GitHub
parent 03196fa110
commit 20c4121f25
28 changed files with 584 additions and 427 deletions

View File

@@ -1,11 +1,6 @@
#include <core/check.h>
#include <core/record.h>
#include <furi.h> #include <furi.h>
#include <stdint.h> #include <stdint.h>
#include <FreeRTOS.h>
#include <semphr.h>
#include <rpc/rpc.h> #include <rpc/rpc.h>
#include <rpc/rpc_i.h> #include <rpc/rpc_i.h>
#include <cli/cli.h> #include <cli/cli.h>
@@ -40,8 +35,8 @@ static uint32_t command_id = 0;
typedef struct { typedef struct {
RpcSession* session; RpcSession* session;
FuriStreamBuffer* output_stream; FuriStreamBuffer* output_stream;
SemaphoreHandle_t close_session_semaphore; FuriSemaphore* close_session_semaphore;
SemaphoreHandle_t terminate_semaphore; FuriSemaphore* terminate_semaphore;
uint32_t timeout; uint32_t timeout;
} RpcSessionContext; } RpcSessionContext;
@@ -96,8 +91,8 @@ static void test_rpc_setup(void) {
rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1); rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1);
rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback); rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback);
rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary(); rpc_session[0].close_session_semaphore = furi_semaphore_alloc(1, 0);
rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary(); rpc_session[0].terminate_semaphore = furi_semaphore_alloc(1, 0);
rpc_session_set_close_callback(rpc_session[0].session, test_rpc_session_close_callback); rpc_session_set_close_callback(rpc_session[0].session, test_rpc_session_close_callback);
rpc_session_set_terminated_callback( rpc_session_set_terminated_callback(
rpc_session[0].session, test_rpc_session_terminated_callback); rpc_session[0].session, test_rpc_session_terminated_callback);
@@ -116,8 +111,8 @@ static void test_rpc_setup_second_session(void) {
rpc_session[1].output_stream = furi_stream_buffer_alloc(1000, 1); rpc_session[1].output_stream = furi_stream_buffer_alloc(1000, 1);
rpc_session_set_send_bytes_callback(rpc_session[1].session, output_bytes_callback); rpc_session_set_send_bytes_callback(rpc_session[1].session, output_bytes_callback);
rpc_session[1].close_session_semaphore = xSemaphoreCreateBinary(); rpc_session[1].close_session_semaphore = furi_semaphore_alloc(1, 0);
rpc_session[1].terminate_semaphore = xSemaphoreCreateBinary(); rpc_session[1].terminate_semaphore = furi_semaphore_alloc(1, 0);
rpc_session_set_close_callback(rpc_session[1].session, test_rpc_session_close_callback); rpc_session_set_close_callback(rpc_session[1].session, test_rpc_session_close_callback);
rpc_session_set_terminated_callback( rpc_session_set_terminated_callback(
rpc_session[1].session, test_rpc_session_terminated_callback); rpc_session[1].session, test_rpc_session_terminated_callback);
@@ -126,13 +121,15 @@ static void test_rpc_setup_second_session(void) {
static void test_rpc_teardown(void) { static void test_rpc_teardown(void) {
furi_check(rpc_session[0].close_session_semaphore); furi_check(rpc_session[0].close_session_semaphore);
xSemaphoreTake(rpc_session[0].terminate_semaphore, 0); furi_semaphore_acquire(rpc_session[0].terminate_semaphore, 0);
rpc_session_close(rpc_session[0].session); rpc_session_close(rpc_session[0].session);
furi_check(xSemaphoreTake(rpc_session[0].terminate_semaphore, portMAX_DELAY)); furi_check(
furi_semaphore_acquire(rpc_session[0].terminate_semaphore, FuriWaitForever) ==
FuriStatusOk);
furi_record_close(RECORD_RPC); furi_record_close(RECORD_RPC);
furi_stream_buffer_free(rpc_session[0].output_stream); furi_stream_buffer_free(rpc_session[0].output_stream);
vSemaphoreDelete(rpc_session[0].close_session_semaphore); furi_semaphore_free(rpc_session[0].close_session_semaphore);
vSemaphoreDelete(rpc_session[0].terminate_semaphore); furi_semaphore_free(rpc_session[0].terminate_semaphore);
++command_id; ++command_id;
rpc_session[0].output_stream = NULL; rpc_session[0].output_stream = NULL;
rpc_session[0].close_session_semaphore = NULL; rpc_session[0].close_session_semaphore = NULL;
@@ -142,12 +139,14 @@ static void test_rpc_teardown(void) {
static void test_rpc_teardown_second_session(void) { static void test_rpc_teardown_second_session(void) {
furi_check(rpc_session[1].close_session_semaphore); furi_check(rpc_session[1].close_session_semaphore);
xSemaphoreTake(rpc_session[1].terminate_semaphore, 0); furi_semaphore_acquire(rpc_session[1].terminate_semaphore, 0);
rpc_session_close(rpc_session[1].session); rpc_session_close(rpc_session[1].session);
furi_check(xSemaphoreTake(rpc_session[1].terminate_semaphore, portMAX_DELAY)); furi_check(
furi_semaphore_acquire(rpc_session[1].terminate_semaphore, FuriWaitForever) ==
FuriStatusOk);
furi_stream_buffer_free(rpc_session[1].output_stream); furi_stream_buffer_free(rpc_session[1].output_stream);
vSemaphoreDelete(rpc_session[1].close_session_semaphore); furi_semaphore_free(rpc_session[1].close_session_semaphore);
vSemaphoreDelete(rpc_session[1].terminate_semaphore); furi_semaphore_free(rpc_session[1].terminate_semaphore);
++command_id; ++command_id;
rpc_session[1].output_stream = NULL; rpc_session[1].output_stream = NULL;
rpc_session[1].close_session_semaphore = NULL; rpc_session[1].close_session_semaphore = NULL;
@@ -204,14 +203,14 @@ static void test_rpc_session_close_callback(void* context) {
furi_check(context); furi_check(context);
RpcSessionContext* callbacks_context = context; RpcSessionContext* callbacks_context = context;
xSemaphoreGive(callbacks_context->close_session_semaphore); furi_check(furi_semaphore_release(callbacks_context->close_session_semaphore) == FuriStatusOk);
} }
static void test_rpc_session_terminated_callback(void* context) { static void test_rpc_session_terminated_callback(void* context) {
furi_check(context); furi_check(context);
RpcSessionContext* callbacks_context = context; RpcSessionContext* callbacks_context = context;
xSemaphoreGive(callbacks_context->terminate_semaphore); furi_check(furi_semaphore_release(callbacks_context->terminate_semaphore) == FuriStatusOk);
} }
static void test_rpc_print_message_list(MsgList_t msg_list) { static void test_rpc_print_message_list(MsgList_t msg_list) {
@@ -1645,7 +1644,7 @@ static void test_rpc_feed_rubbish_run(
test_rpc_add_empty_to_list(expected, PB_CommandStatus_ERROR_DECODE, 0); test_rpc_add_empty_to_list(expected, PB_CommandStatus_ERROR_DECODE, 0);
furi_check(!xSemaphoreTake(rpc_session[0].close_session_semaphore, 0)); furi_check(furi_semaphore_acquire(rpc_session[0].close_session_semaphore, 0) != FuriStatusOk);
test_rpc_encode_and_feed(input_before, 0); test_rpc_encode_and_feed(input_before, 0);
test_send_rubbish(rpc_session[0].session, pattern, pattern_size, size); test_send_rubbish(rpc_session[0].session, pattern, pattern_size, size);
test_rpc_encode_and_feed(input_after, 0); test_rpc_encode_and_feed(input_after, 0);

View File

@@ -19,9 +19,9 @@ static constexpr auto unit_tests_api_table = sort(create_array_t<sym_entry>(
API_METHOD(xQueueSemaphoreTake, BaseType_t, (QueueHandle_t, TickType_t)), API_METHOD(xQueueSemaphoreTake, BaseType_t, (QueueHandle_t, TickType_t)),
API_METHOD(vQueueDelete, void, (QueueHandle_t)), API_METHOD(vQueueDelete, void, (QueueHandle_t)),
API_METHOD( API_METHOD(
xQueueGenericCreate, xQueueGenericCreateStatic,
QueueHandle_t, QueueHandle_t,
(const UBaseType_t, const UBaseType_t, const uint8_t)), (const UBaseType_t, const UBaseType_t, uint8_t*, StaticQueue_t*, const uint8_t)),
API_METHOD( API_METHOD(
xQueueGenericSend, xQueueGenericSend,
BaseType_t, BaseType_t,

View File

@@ -435,6 +435,8 @@ int32_t bt_srv(void* p) {
FURI_LOG_W(TAG, "Skipping start in special boot mode"); FURI_LOG_W(TAG, "Skipping start in special boot mode");
ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT); ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT);
furi_record_create(RECORD_BT, bt); furi_record_create(RECORD_BT, bt);
furi_thread_suspend(furi_thread_get_current_id());
return 0; return 0;
} }

View File

@@ -441,6 +441,8 @@ int32_t desktop_srv(void* p) {
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
FURI_LOG_W(TAG, "Skipping start in special boot mode"); FURI_LOG_W(TAG, "Skipping start in special boot mode");
furi_thread_suspend(furi_thread_get_current_id());
return 0; return 0;
} }

View File

@@ -1,5 +1,4 @@
#include <furi.h> #include <furi.h>
#include <FreeRTOS.h>
#include <gui/scene_manager.h> #include <gui/scene_manager.h>
#include "../desktop_i.h" #include "../desktop_i.h"

View File

@@ -2,7 +2,6 @@
#include <furi.h> #include <furi.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <FreeRTOS.h>
#include <projdefs.h> #include <projdefs.h>
#include <input/input.h> #include <input/input.h>
#include <gui/canvas.h> #include <gui/canvas.h>

View File

@@ -148,6 +148,8 @@ int32_t dolphin_srv(void* p) {
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
FURI_LOG_W(TAG, "Skipping start in special boot mode"); FURI_LOG_W(TAG, "Skipping start in special boot mode");
furi_thread_suspend(furi_thread_get_current_id());
return 0; return 0;
} }

View File

@@ -198,6 +198,8 @@ int32_t power_srv(void* p) {
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
FURI_LOG_W(TAG, "Skipping start in special boot mode"); FURI_LOG_W(TAG, "Skipping start in special boot mode");
furi_thread_suspend(furi_thread_get_current_id());
return 0; return 0;
} }

View File

@@ -8,18 +8,27 @@
#define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U #define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U
#define FURI_EVENT_FLAG_INVALID_BITS (~((1UL << FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U)) #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) { FuriEventFlag* furi_event_flag_alloc(void) {
furi_check(!FURI_IS_IRQ_MODE()); furi_check(!FURI_IS_IRQ_MODE());
EventGroupHandle_t handle = xEventGroupCreate(); FuriEventFlag* instance = malloc(sizeof(FuriEventFlag));
furi_check(handle);
return ((FuriEventFlag*)handle); furi_check(xEventGroupCreateStatic(&instance->container) == (EventGroupHandle_t)instance);
return instance;
} }
void furi_event_flag_free(FuriEventFlag* instance) { void furi_event_flag_free(FuriEventFlag* instance) {
furi_check(!FURI_IS_IRQ_MODE()); furi_check(!FURI_IS_IRQ_MODE());
vEventGroupDelete((EventGroupHandle_t)instance); vEventGroupDelete((EventGroupHandle_t)instance);
free(instance);
} }
uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { 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 event flags after setting */
return (rflags); return rflags;
} }
uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) { 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 event flags before clearing */
return (rflags); return rflags;
} }
uint32_t furi_event_flag_get(FuriEventFlag* instance) { 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 current event flags */
return (rflags); return rflags;
} }
uint32_t furi_event_flag_wait( uint32_t furi_event_flag_wait(
@@ -136,5 +145,5 @@ uint32_t furi_event_flag_wait(
} }
/* Return event flags before clearing */ /* Return event flags before clearing */
return (rflags); return rflags;
} }

View File

@@ -10,7 +10,7 @@
extern "C" { extern "C" {
#endif #endif
typedef void FuriEventFlag; typedef struct FuriEventFlag FuriEventFlag;
/** Allocate FuriEventFlag /** Allocate FuriEventFlag
* *

View File

@@ -56,10 +56,6 @@ task.h is included from an application file. */
#error This feature is broken, logging transport must be replaced with RTT #error This feature is broken, logging transport must be replaced with RTT
#endif #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. */ /* Block sizes must not get too small. */
#define heapMINIMUM_BLOCK_SIZE ((size_t)(xHeapStructSize << 1)) #define heapMINIMUM_BLOCK_SIZE ((size_t)(xHeapStructSize << 1))

View File

@@ -5,14 +5,21 @@
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <queue.h> #include <queue.h>
struct FuriMessageQueue { // Internal FreeRTOS member names
// !!! Semi-Opaque type inheritance, Very Fragile, DO NOT MOVE !!! #define uxMessagesWaiting uxDummy4[0]
StaticQueue_t container; #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[]; 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) { 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)); 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) { 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) { uint32_t furi_message_queue_get_capacity(FuriMessageQueue* instance) {
furi_check(instance); furi_check(instance);
uint32_t capacity; return instance->container.uxLength;
/* capacity = pxQueue->uxLength */
capacity = instance->container.uxDummy4[1];
/* Return maximum number of messages */
return (capacity);
} }
uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) { uint32_t furi_message_queue_get_message_size(FuriMessageQueue* instance) {
furi_check(instance); furi_check(instance);
uint32_t size; return instance->container.uxItemSize;
/* size = pxQueue->uxItemSize */
size = instance->container.uxDummy4[2];
/* Return maximum message size */
return (size);
} }
uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { 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); 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) { 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) { if(furi_kernel_is_irq_or_masked() != 0U) {
isrm = taskENTER_CRITICAL_FROM_ISR(); isrm = taskENTER_CRITICAL_FROM_ISR();
/* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ space = instance->container.uxLength - instance->container.uxMessagesWaiting;
space = instance->container.uxDummy4[1] - instance->container.uxDummy4[0];
taskEXIT_CRITICAL_FROM_ISR(isrm); taskEXIT_CRITICAL_FROM_ISR(isrm);
} else { } else {
space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)instance); space = (uint32_t)uxQueueSpacesAvailable((QueueHandle_t)instance);
} }
/* Return number of available slots */ return space;
return (space);
} }
FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) {
@@ -191,6 +182,5 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) {
(void)xQueueReset(hQueue); (void)xQueueReset(hQueue);
} }
/* Return execution status */ return stat;
return (stat);
} }

View File

@@ -5,129 +5,120 @@
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <semphr.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) { FuriMutex* furi_mutex_alloc(FuriMutexType type) {
furi_check(!FURI_IS_IRQ_MODE()); furi_check(!FURI_IS_IRQ_MODE());
SemaphoreHandle_t hMutex = NULL; FuriMutex* instance = malloc(sizeof(FuriMutex));
SemaphoreHandle_t hMutex;
if(type == FuriMutexTypeNormal) { if(type == FuriMutexTypeNormal) {
hMutex = xSemaphoreCreateMutex(); hMutex = xSemaphoreCreateMutexStatic(&instance->container);
} else if(type == FuriMutexTypeRecursive) { } else if(type == FuriMutexTypeRecursive) {
hMutex = xSemaphoreCreateRecursiveMutex(); hMutex = xSemaphoreCreateRecursiveMutexStatic(&instance->container);
} else { } else {
furi_crash(); furi_crash();
} }
furi_check(hMutex != NULL); furi_check(hMutex == (SemaphoreHandle_t)instance);
if(type == FuriMutexTypeRecursive) { return instance;
/* Set LSB as 'recursive mutex flag' */
hMutex = (SemaphoreHandle_t)((uint32_t)hMutex | 1U);
}
/* Return mutex ID */
return ((FuriMutex*)hMutex);
} }
void furi_mutex_free(FuriMutex* instance) { void furi_mutex_free(FuriMutex* instance) {
furi_check(!FURI_IS_IRQ_MODE()); furi_check(!FURI_IS_IRQ_MODE());
furi_check(instance); 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) { FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) {
furi_check(instance); furi_check(instance);
SemaphoreHandle_t hMutex; SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance);
FuriStatus stat; const uint8_t mutex_type = instance->container.ucQueueType;
uint32_t rmtx;
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); FuriStatus stat = FuriStatusOk;
/* Extract recursive mutex flag */
rmtx = (uint32_t)instance & 1U;
stat = FuriStatusOk;
if(FURI_IS_IRQ_MODE()) { if(FURI_IS_IRQ_MODE()) {
stat = FuriStatusErrorISR; stat = FuriStatusErrorISR;
} else if(hMutex == NULL) {
stat = FuriStatusErrorParameter; } else if(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
} else { if(xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) {
if(rmtx != 0U) { if(timeout != 0U) {
if(xSemaphoreTakeRecursive(hMutex, timeout) != pdPASS) { stat = FuriStatusErrorTimeout;
if(timeout != 0U) { } else {
stat = FuriStatusErrorTimeout; stat = FuriStatusErrorResource;
} else {
stat = FuriStatusErrorResource;
}
}
} else {
if(xSemaphoreTake(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) { FuriStatus furi_mutex_release(FuriMutex* instance) {
furi_check(instance); furi_check(instance);
SemaphoreHandle_t hMutex; SemaphoreHandle_t hMutex = (SemaphoreHandle_t)(instance);
FuriStatus stat; const uint8_t mutex_type = instance->container.ucQueueType;
uint32_t rmtx;
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); FuriStatus stat = FuriStatusOk;
/* Extract recursive mutex flag */
rmtx = (uint32_t)instance & 1U;
stat = FuriStatusOk;
if(FURI_IS_IRQ_MODE()) { if(FURI_IS_IRQ_MODE()) {
stat = FuriStatusErrorISR; stat = FuriStatusErrorISR;
} else if(hMutex == NULL) {
stat = FuriStatusErrorParameter; } else if(mutex_type == queueQUEUE_TYPE_RECURSIVE_MUTEX) {
} else { if(xSemaphoreGiveRecursive(hMutex) != pdPASS) {
if(rmtx != 0U) { stat = FuriStatusErrorResource;
if(xSemaphoreGiveRecursive(hMutex) != pdPASS) {
stat = FuriStatusErrorResource;
}
} else {
if(xSemaphoreGive(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) { FuriThreadId furi_mutex_get_owner(FuriMutex* instance) {
furi_check(instance); furi_check(instance);
SemaphoreHandle_t hMutex; SemaphoreHandle_t hMutex = (SemaphoreHandle_t)instance;
FuriThreadId owner; FuriThreadId owner;
hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); if(FURI_IS_IRQ_MODE()) {
if(hMutex == NULL) {
owner = 0;
} else if(FURI_IS_IRQ_MODE()) {
owner = (FuriThreadId)xSemaphoreGetMutexHolderFromISR(hMutex); owner = (FuriThreadId)xSemaphoreGetMutexHolderFromISR(hMutex);
} else { } else {
owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex);
} }
/* Return owner thread ID */ return owner;
return (owner);
} }

View File

@@ -16,7 +16,7 @@ typedef enum {
FuriMutexTypeRecursive, FuriMutexTypeRecursive,
} FuriMutexType; } FuriMutexType;
typedef void FuriMutex; typedef struct FuriMutex FuriMutex;
/** Allocate FuriMutex /** Allocate FuriMutex
* *

View File

@@ -5,36 +5,43 @@
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <semphr.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) { FuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count) {
furi_check(!FURI_IS_IRQ_MODE()); furi_check(!FURI_IS_IRQ_MODE());
furi_check((max_count > 0U) && (initial_count <= max_count)); 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) { if(max_count == 1U) {
hSemaphore = xSemaphoreCreateBinary(); hSemaphore = xSemaphoreCreateBinaryStatic(&instance->container);
if((hSemaphore != NULL) && (initial_count != 0U)) {
if(xSemaphoreGive(hSemaphore) != pdPASS) {
vSemaphoreDelete(hSemaphore);
hSemaphore = NULL;
}
}
} else { } 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 */ if(max_count == 1U && initial_count != 0U) {
return ((FuriSemaphore*)hSemaphore); furi_check(xSemaphoreGive(hSemaphore) == pdPASS);
}
return instance;
} }
void furi_semaphore_free(FuriSemaphore* instance) { void furi_semaphore_free(FuriSemaphore* instance) {
furi_check(instance); furi_check(instance);
furi_check(!FURI_IS_IRQ_MODE()); furi_check(!FURI_IS_IRQ_MODE());
SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; vSemaphoreDelete((SemaphoreHandle_t)instance);
free(instance);
vSemaphoreDelete(hSemaphore);
} }
FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) { 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); portYIELD_FROM_ISR(yield);
} }
} }
} else { } else {
if(xSemaphoreTake(hSemaphore, (TickType_t)timeout) != pdPASS) { if(xSemaphoreTake(hSemaphore, (TickType_t)timeout) != pdPASS) {
if(timeout != 0U) { 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) { FuriStatus furi_semaphore_release(FuriSemaphore* instance) {
@@ -89,14 +96,14 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) {
} else { } else {
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
} }
} else { } else {
if(xSemaphoreGive(hSemaphore) != pdPASS) { if(xSemaphoreGive(hSemaphore) != pdPASS) {
stat = FuriStatusErrorResource; stat = FuriStatusErrorResource;
} }
} }
/* Return execution status */ return stat;
return (stat);
} }
uint32_t furi_semaphore_get_count(FuriSemaphore* instance) { 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); count = (uint32_t)uxSemaphoreGetCount(hSemaphore);
} }
/* Return number of tokens */ return count;
return (count);
} }

View File

@@ -11,7 +11,7 @@
extern "C" { extern "C" {
#endif #endif
typedef void FuriSemaphore; typedef struct FuriSemaphore FuriSemaphore;
/** Allocate semaphore /** Allocate semaphore
* *

View File

@@ -6,24 +6,42 @@
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <FreeRTOS-Kernel/include/stream_buffer.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) { FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level) {
furi_check(size != 0); furi_check(size != 0);
StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level); // Actual FreeRTOS usable buffer size seems to be one less
furi_check(handle); 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) { void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer) {
furi_check(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) { bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level) {
furi_check(stream_buffer); 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( size_t furi_stream_buffer_send(
@@ -37,10 +55,10 @@ size_t furi_stream_buffer_send(
if(FURI_IS_IRQ_MODE()) { if(FURI_IS_IRQ_MODE()) {
BaseType_t yield; BaseType_t yield;
ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield); ret = xStreamBufferSendFromISR((StreamBufferHandle_t)stream_buffer, data, length, &yield);
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
} else { } else {
ret = xStreamBufferSend(stream_buffer, data, length, timeout); ret = xStreamBufferSend((StreamBufferHandle_t)stream_buffer, data, length, timeout);
} }
return ret; return ret;
@@ -57,10 +75,11 @@ size_t furi_stream_buffer_receive(
if(FURI_IS_IRQ_MODE()) { if(FURI_IS_IRQ_MODE()) {
BaseType_t yield; BaseType_t yield;
ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield); ret =
xStreamBufferReceiveFromISR((StreamBufferHandle_t)stream_buffer, data, length, &yield);
portYIELD_FROM_ISR(yield); portYIELD_FROM_ISR(yield);
} else { } else {
ret = xStreamBufferReceive(stream_buffer, data, length, timeout); ret = xStreamBufferReceive((StreamBufferHandle_t)stream_buffer, data, length, timeout);
} }
return ret; return ret;
@@ -69,33 +88,33 @@ size_t furi_stream_buffer_receive(
size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer) { size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer) {
furi_check(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) { size_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer) {
furi_check(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) { bool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer) {
furi_check(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) { bool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer) {
furi_check(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) { FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer) {
furi_check(stream_buffer); furi_check(stream_buffer);
if(xStreamBufferReset(stream_buffer) == pdPASS) { if(xStreamBufferReset((StreamBufferHandle_t)stream_buffer) == pdPASS) {
return FuriStatusOk; return FuriStatusOk;
} else { } else {
return FuriStatusError; return FuriStatusError;
} }
} }

View File

@@ -19,7 +19,7 @@
extern "C" { extern "C" {
#endif #endif
typedef void FuriStreamBuffer; typedef struct FuriStreamBuffer FuriStreamBuffer;
/** /**
* @brief Allocate stream buffer instance. * @brief Allocate stream buffer instance.

View File

@@ -27,6 +27,10 @@ struct FuriThreadStdout {
}; };
struct FuriThread { struct FuriThread {
StaticTask_t container;
TaskHandle_t task_handle;
StackType_t* stack_buffer;
FuriThreadState state; FuriThreadState state;
int32_t ret; int32_t ret;
@@ -41,7 +45,7 @@ struct FuriThread {
FuriThreadPriority priority; FuriThreadPriority priority;
TaskHandle_t task_handle; size_t stack_size;
size_t heap_size; size_t heap_size;
FuriThreadStdout output; FuriThreadStdout output;
@@ -50,10 +54,11 @@ struct FuriThread {
// this ensures that the size of this structure is minimal // this ensures that the size of this structure is minimal
bool is_service; bool is_service;
bool heap_trace_enabled; 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 size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size);
static int32_t __furi_thread_stdout_flush(FuriThread* thread); 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); thread->ret = thread->callback(thread->context);
furi_check(!thread->is_service, "Service threads MUST NOT return");
if(thread->heap_trace_enabled == true) { if(thread->heap_trace_enabled == true) {
furi_delay_ms(33); furi_delay_ms(33);
thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); 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); 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 // flush stdout
__furi_thread_stdout_flush(thread); __furi_thread_stdout_flush(thread);
@@ -122,10 +122,8 @@ static void furi_thread_body(void* context) {
furi_thread_catch(); furi_thread_catch();
} }
FuriThread* furi_thread_alloc(void) { static void furi_thread_init_common(FuriThread* thread) {
FuriThread* thread = malloc(sizeof(FuriThread));
thread->output.buffer = furi_string_alloc(); thread->output.buffer = furi_string_alloc();
thread->is_service = false;
FuriThread* parent = NULL; FuriThread* parent = NULL;
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
@@ -150,6 +148,32 @@ FuriThread* furi_thread_alloc(void) {
} else { } else {
thread->heap_trace_enabled = false; 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; return thread;
} }
@@ -169,15 +193,20 @@ FuriThread* furi_thread_alloc_ex(
void furi_thread_free(FuriThread* thread) { void furi_thread_free(FuriThread* thread) {
furi_check(thread); furi_check(thread);
// Cannot free a service thread
// Ensure that use join before free furi_check(thread->is_service == false);
// Cannot free a non-joined thread
furi_check(thread->state == FuriThreadStateStopped); furi_check(thread->state == FuriThreadStateStopped);
furi_check(thread->task_handle == NULL); furi_check(thread->task_handle == NULL);
if(thread->name) free(thread->name); furi_thread_set_name(thread, NULL);
if(thread->appid) free(thread->appid); furi_thread_set_appid(thread, NULL);
furi_string_free(thread->output.buffer);
if(thread->stack_buffer) {
free(thread->stack_buffer);
}
furi_string_free(thread->output.buffer);
free(thread); free(thread);
} }
@@ -185,7 +214,9 @@ void furi_thread_set_name(FuriThread* thread, const char* name) {
furi_check(thread); furi_check(thread);
furi_check(thread->state == FuriThreadStateStopped); furi_check(thread->state == FuriThreadStateStopped);
if(thread->name) free(thread->name); if(thread->name) {
free(thread->name);
}
thread->name = name ? strdup(name) : NULL; 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) { void furi_thread_set_appid(FuriThread* thread, const char* appid) {
furi_check(thread); furi_check(thread);
furi_check(thread->state == FuriThreadStateStopped); 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) { if(thread->appid) {
thread->is_service = true; free(thread->appid);
}
thread->appid = appid ? strdup(appid) : NULL;
} }
void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) { void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) {
furi_check(thread); furi_check(thread);
furi_check(thread->state == FuriThreadStateStopped); 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 <= 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; thread->stack_size = stack_size;
} }
@@ -270,24 +310,19 @@ void furi_thread_start(FuriThread* thread) {
furi_thread_set_state(thread, FuriThreadStateStarting); 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; 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) { 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) { bool furi_thread_join(FuriThread* thread) {
furi_check(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); furi_check(furi_thread_get_current() != thread);
// !!! IMPORTANT NOTICE !!! // !!! IMPORTANT NOTICE !!!
@@ -390,7 +427,7 @@ uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags) {
} }
} }
/* Return flags after setting */ /* Return flags after setting */
return (rflags); return rflags;
} }
uint32_t furi_thread_flags_clear(uint32_t flags) { 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 flags before clearing */
return (rflags); return rflags;
} }
uint32_t furi_thread_flags_get(void) { 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) { 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 flags before clearing */
return (rflags); return rflags;
} }
uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count) { 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); vPortFree(task);
} }
return (count); return count;
} }
const char* furi_thread_get_name(FuriThreadId thread_id) { 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); name = pcTaskGetName(hTask);
} }
return (name); return name;
} }
const char* furi_thread_get_appid(FuriThreadId thread_id) { 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) { 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)); 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) { static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) {

View File

@@ -1,6 +1,6 @@
/** /**
* @file thread.h * @file thread.h
* Furi: Furi Thread API * @brief Furi: Furi Thread API
*/ */
#pragma once #pragma once
@@ -15,14 +15,20 @@
extern "C" { extern "C" {
#endif #endif
/** FuriThreadState */ /**
* @brief Enumeration of possible FuriThread states.
*
* Many of the FuriThread functions MUST ONLY be called when the thread is STOPPED.
*/
typedef enum { typedef enum {
FuriThreadStateStopped, FuriThreadStateStopped, /**< Thread is stopped */
FuriThreadStateStarting, FuriThreadStateStarting, /**< Thread is starting */
FuriThreadStateRunning, FuriThreadStateRunning, /**< Thread is running */
} FuriThreadState; } FuriThreadState;
/** FuriThreadPriority */ /**
* @brief Enumeration of possible FuriThread priorities.
*/
typedef enum { typedef enum {
FuriThreadPriorityNone = 0, /**< Uninitialized, choose system default */ FuriThreadPriorityNone = 0, /**< Uninitialized, choose system default */
FuriThreadPriorityIdle = 1, /**< Idle priority */ FuriThreadPriorityIdle = 1, /**< Idle priority */
@@ -35,42 +41,85 @@ typedef enum {
(FURI_CONFIG_THREAD_MAX_PRIORITIES - 1), /**< Deferred ISR (highest possible) */ (FURI_CONFIG_THREAD_MAX_PRIORITIES - 1), /**< Deferred ISR (highest possible) */
} FuriThreadPriority; } FuriThreadPriority;
/** FuriThread anonymous structure */ /**
* @brief FuriThread opaque type.
*/
typedef struct FuriThread FuriThread; 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; 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); typedef int32_t (*FuriThreadCallback)(void* context);
/** Write to stdout callback /**
* @param data pointer to data * @brief Standard output callback function pointer type.
* @param size data size @warning your handler must consume everything *
* 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); typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size);
/** FuriThread state change callback called upon thread state change /**
* @param state new thread state * @brief State change callback function pointer type.
* @param context callback context *
* 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); 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); 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[in] name human-readable thread name (can be NULL)
* @param stack_size * @param[in] stack_size stack size in bytes (can be changed later)
* @param callback * @param[in] callback pointer to a function to be executed in this thread
* @param context * @param[in] context pointer to a user-specified object (will be passed to the callback)
* @return FuriThread* * @return pointer to the created FuriThread instance
*/ */
FuriThread* furi_thread_alloc_ex( FuriThread* furi_thread_alloc_ex(
const char* name, const char* name,
@@ -78,261 +127,339 @@ FuriThread* furi_thread_alloc_ex(
FuriThreadCallback callback, FuriThreadCallback callback,
void* context); 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); void furi_thread_free(FuriThread* thread);
/** Set FuriThread name /**
* @brief Set the name of a FuriThread instance.
* *
* @param thread FuriThread instance * The thread MUST be stopped when calling this function.
* @param name string *
* @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); 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. * 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. * All threads spawned by the same app will have the same appid.
* *
* @param thread * @param[in,out] thread pointer to the FuriThread instance to be modified
* @param appid * @param[in] appid thread application ID (can be NULL)
*/ */
void furi_thread_set_appid(FuriThread* thread, const char* appid); 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 * @brief Set the stack size of a FuriThread instance.
*
* @param thread
*/
void furi_thread_mark_as_service(FuriThread* thread);
/** Set FuriThread stack size
* *
* @param thread FuriThread instance * The thread MUST be stopped when calling this function. Additionally, it is NOT possible
* @param stack_size stack size in bytes * 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); 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 * The thread MUST be stopped when calling this function.
* @param callback FuriThreadCallback, called upon thread run *
* @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); void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback);
/** Set FuriThread context /**
* @brief Set the callback function context.
* *
* @param thread FuriThread instance * The thread MUST be stopped when calling this function.
* @param context pointer to context for thread callback *
* @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); void furi_thread_set_context(FuriThread* thread, void* context);
/** Set FuriThread priority /**
* @brief Set the priority of a FuriThread.
* *
* @param thread FuriThread instance * The thread MUST be stopped when calling this function.
* @param priority FuriThreadPriority value *
* @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); void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority);
/** Get FuriThread priority /**
* @brief Get the priority of a FuriThread.
* *
* @param thread FuriThread instance * @param[in] thread pointer to the FuriThread instance to be queried
* @return FuriThreadPriority value * @return priority level value
*/ */
FuriThreadPriority furi_thread_get_priority(FuriThread* thread); 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); 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); 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 * The thread MUST be stopped when calling this function.
* @param callback state change callback *
* @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); 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 * The thread MUST be stopped when calling this function.
* @param context pointer to context *
* @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); 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 * @param[in] thread pointer to the FuriThread instance to be queried
* * @return thread state value
* @return thread state from FuriThreadState
*/ */
FuriThreadState furi_thread_get_state(FuriThread* thread); 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); 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 * The thread callback function must return in order for the FuriThread instance to become joinable.
* control), otherwise it will wait forever.
* *
* @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); 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 * @param[in] thread pointer to the FuriThread instance to be queried
* * @return unique identifier value or NULL if thread is not running
* @return FuriThreadId or NULL
*/ */
FuriThreadId furi_thread_get_id(FuriThread* thread); 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); 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); 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); 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); int32_t furi_thread_get_return_code(FuriThread* thread);
/** Thread related methods that doesn't involve FuriThread directly */ /**
* @brief Get the unique identifier of the current FuriThread.
/** Get FreeRTOS FuriThreadId for current thread
* *
* @return FuriThreadId or NULL * @return unique identifier value
*/ */
FuriThreadId furi_thread_get_current_id(void); 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); FuriThread* furi_thread_get_current(void);
/** Return control to scheduler */ /**
* @brief Return control to the scheduler.
*/
void furi_thread_yield(void); 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); 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); 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); 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); 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[out] thread_array pointer to the output array (must be properly allocated)
* @param array_item_count array size * @param[in] array_item_count output array capacity in elements (NOT bytes)
* @return uint32_t threads count * @return total thread count (array_item_count or less)
*/ */
uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_item_count); 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 * @param[in] thread_id unique identifier of the thread to be queried
* @return const char* name or NULL * @return pointer to a zero-terminated string or NULL
*/ */
const char* furi_thread_get_name(FuriThreadId thread_id); 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 * @param[in] thread_id unique identifier of the thread to be queried
* @return const char* appid * @return pointer to a zero-terminated string
*/ */
const char* furi_thread_get_appid(FuriThreadId thread_id); const char* furi_thread_get_appid(FuriThreadId thread_id);
/** /**
* @brief Get thread stack watermark * @brief Get thread stack watermark.
* *
* @param thread_id * @param[in] thread_id unique identifier of the thread to be queried
* @return uint32_t * @return stack watermark value
*/ */
uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); 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); 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); void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback);
/** Write data to buffered STDOUT /** Write data to buffered standard output.
* *
* @param data input data * @param[in] data pointer to the data to be written
* @param size input data size * @param[in] size data size in bytes
* * @return number of bytes that was actually written
* @return size_t written data size
*/ */
size_t furi_thread_stdout_write(const char* data, size_t size); 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); 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); 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); void furi_thread_resume(FuriThreadId thread_id);
/** Get thread suspended state /**
* @brief Test if a thread is suspended.
* *
* @param thread_id thread id * @param[in] thread_id unique identifier of the thread to be queried
* @return true if thread is suspended * @return true if thread is suspended, false otherwise
*/ */
bool furi_thread_is_suspended(FuriThreadId thread_id); bool furi_thread_is_suspended(FuriThreadId thread_id);

View File

@@ -5,56 +5,46 @@
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <timers.h> #include <timers.h>
typedef struct { struct FuriTimer {
FuriTimerCallback func; StaticTimer_t container;
void* context; FuriTimerCallback cb_func;
} TimerCallback_t; 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) { static void TimerCallback(TimerHandle_t hTimer) {
TimerCallback_t* callb; FuriTimer* instance = pvTimerGetTimerID(hTimer);
furi_check(instance);
/* Retrieve pointer to callback function and context */ instance->cb_func(instance->cb_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* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) { FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) {
furi_check((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL)); furi_check((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL));
TimerHandle_t hTimer; FuriTimer* instance = malloc(sizeof(FuriTimer));
TimerCallback_t* callb;
UBaseType_t reload;
hTimer = NULL; instance->cb_func = func;
instance->cb_context = context;
/* Dynamic memory allocation is available: if memory for callback and */ const UBaseType_t reload = (type == FuriTimerTypeOnce ? pdFALSE : pdTRUE);
/* its context is not provided, allocate it from dynamic memory pool */ const TimerHandle_t hTimer = xTimerCreateStatic(
callb = (TimerCallback_t*)malloc(sizeof(TimerCallback_t)); NULL, portMAX_DELAY, reload, instance, TimerCallback, &instance->container);
callb->func = func; furi_check(hTimer == (TimerHandle_t)instance);
callb->context = context;
if(type == FuriTimerTypeOnce) { return instance;
reload = pdFALSE; }
} else {
reload = pdTRUE;
}
/* Store callback memory dynamic allocation flag */ static void furi_timer_epilogue(void* context, uint32_t arg) {
callb = (TimerCallback_t*)((uint32_t)callb | 1U); furi_assert(context);
// TimerCallback function is always provided as a callback and is used to call application UNUSED(arg);
// specified function with its context both stored in structure callb.
hTimer = xTimerCreate(NULL, portMAX_DELAY, reload, callb, TimerCallback);
furi_check(hTimer);
/* Return timer ID */ FuriTimer* instance = context;
return ((FuriTimer*)hTimer);
instance->can_be_removed = true;
} }
void furi_timer_free(FuriTimer* instance) { void furi_timer_free(FuriTimer* instance) {
@@ -62,26 +52,14 @@ void furi_timer_free(FuriTimer* instance) {
furi_check(instance); furi_check(instance);
TimerHandle_t hTimer = (TimerHandle_t)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); while(!instance->can_be_removed) {
furi_delay_tick(2);
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);
} }
furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); free(instance);
} }
FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {
@@ -98,8 +76,7 @@ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {
stat = FuriStatusErrorResource; stat = FuriStatusErrorResource;
} }
/* Return execution status */ return stat;
return (stat);
} }
FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) { FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) {
@@ -117,8 +94,7 @@ FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) {
stat = FuriStatusErrorResource; stat = FuriStatusErrorResource;
} }
/* Return execution status */ return stat;
return (stat);
} }
FuriStatus furi_timer_stop(FuriTimer* instance) { FuriStatus furi_timer_stop(FuriTimer* instance) {

View File

@@ -1,3 +1,7 @@
/**
* @file timer.h
* @brief Furi software Timer API.
*/
#pragma once #pragma once
#include "core/base.h" #include "core/base.h"
@@ -13,7 +17,7 @@ typedef enum {
FuriTimerTypePeriodic = 1 ///< Repeating timer. FuriTimerTypePeriodic = 1 ///< Repeating timer.
} FuriTimerType; } FuriTimerType;
typedef void FuriTimer; typedef struct FuriTimer FuriTimer;
/** Allocate timer /** Allocate timer
* *

View File

@@ -37,12 +37,11 @@ void flipper_init(void) {
for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) {
FURI_LOG_D(TAG, "Starting service %s", FLIPPER_SERVICES[i].name); 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].name,
FLIPPER_SERVICES[i].stack_size, FLIPPER_SERVICES[i].stack_size,
FLIPPER_SERVICES[i].app, FLIPPER_SERVICES[i].app,
NULL); NULL);
furi_thread_mark_as_service(thread);
furi_thread_set_appid(thread, FLIPPER_SERVICES[i].appid); furi_thread_set_appid(thread, FLIPPER_SERVICES[i].appid);
furi_thread_start(thread); furi_thread_start(thread);
@@ -67,4 +66,4 @@ void vApplicationGetTimerTaskMemory(
*tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t)); *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t));
*stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH); *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH);
*stack_size = configTIMER_TASK_STACK_DEPTH; *stack_size = configTIMER_TASK_STACK_DEPTH;
} }

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,63.0,, Version,+,64.0,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
@@ -1583,6 +1583,7 @@ Function,+,furi_string_utf8_push,void,"FuriString*, FuriStringUnicodeValue"
Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list" Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list"
Function,+,furi_thread_alloc,FuriThread*, Function,+,furi_thread_alloc,FuriThread*,
Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*"
Function,-,furi_thread_alloc_service,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*"
Function,-,furi_thread_disable_heap_trace,void,FuriThread* Function,-,furi_thread_disable_heap_trace,void,FuriThread*
Function,+,furi_thread_enable_heap_trace,void,FuriThread* Function,+,furi_thread_enable_heap_trace,void,FuriThread*
Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t" Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t"
@@ -1605,7 +1606,6 @@ Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback,
Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_is_suspended,_Bool,FuriThreadId
Function,+,furi_thread_join,_Bool,FuriThread* Function,+,furi_thread_join,_Bool,FuriThread*
Function,+,furi_thread_mark_as_service,void,FuriThread*
Function,+,furi_thread_resume,void,FuriThreadId Function,+,furi_thread_resume,void,FuriThreadId
Function,+,furi_thread_set_appid,void,"FuriThread*, const char*" Function,+,furi_thread_set_appid,void,"FuriThread*, const char*"
Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
1 entry status name type params
2 Version + 63.0 64.0
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/bt/bt_service/bt_keys_storage.h
5 Header + applications/services/cli/cli.h
1583 Function + furi_string_vprintf int FuriString*, const char[], va_list
1584 Function + furi_thread_alloc FuriThread*
1585 Function + furi_thread_alloc_ex FuriThread* const char*, uint32_t, FuriThreadCallback, void*
1586 Function - furi_thread_alloc_service FuriThread* const char*, uint32_t, FuriThreadCallback, void*
1587 Function - furi_thread_disable_heap_trace void FuriThread*
1588 Function + furi_thread_enable_heap_trace void FuriThread*
1589 Function + furi_thread_enumerate uint32_t FuriThreadId*, uint32_t
1606 Function + furi_thread_get_stdout_callback FuriThreadStdoutWriteCallback
1607 Function + furi_thread_is_suspended _Bool FuriThreadId
1608 Function + furi_thread_join _Bool FuriThread*
Function + furi_thread_mark_as_service void FuriThread*
1609 Function + furi_thread_resume void FuriThreadId
1610 Function + furi_thread_set_appid void FuriThread*, const char*
1611 Function + furi_thread_set_callback void FuriThread*, FuriThreadCallback

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,63.0,, Version,+,64.0,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
@@ -1791,6 +1791,7 @@ Function,+,furi_string_utf8_push,void,"FuriString*, FuriStringUnicodeValue"
Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list" Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list"
Function,+,furi_thread_alloc,FuriThread*, Function,+,furi_thread_alloc,FuriThread*,
Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*"
Function,-,furi_thread_alloc_service,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*"
Function,-,furi_thread_disable_heap_trace,void,FuriThread* Function,-,furi_thread_disable_heap_trace,void,FuriThread*
Function,+,furi_thread_enable_heap_trace,void,FuriThread* Function,+,furi_thread_enable_heap_trace,void,FuriThread*
Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t" Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t"
@@ -1813,7 +1814,6 @@ Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback,
Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_is_suspended,_Bool,FuriThreadId
Function,+,furi_thread_join,_Bool,FuriThread* Function,+,furi_thread_join,_Bool,FuriThread*
Function,+,furi_thread_mark_as_service,void,FuriThread*
Function,+,furi_thread_resume,void,FuriThreadId Function,+,furi_thread_resume,void,FuriThreadId
Function,+,furi_thread_set_appid,void,"FuriThread*, const char*" Function,+,furi_thread_set_appid,void,"FuriThread*, const char*"
Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
1 entry status name type params
2 Version + 63.0 64.0
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/bt/bt_service/bt_keys_storage.h
1791 Function + furi_string_vprintf int FuriString*, const char[], va_list
1792 Function + furi_thread_alloc FuriThread*
1793 Function + furi_thread_alloc_ex FuriThread* const char*, uint32_t, FuriThreadCallback, void*
1794 Function - furi_thread_alloc_service FuriThread* const char*, uint32_t, FuriThreadCallback, void*
1795 Function - furi_thread_disable_heap_trace void FuriThread*
1796 Function + furi_thread_enable_heap_trace void FuriThread*
1797 Function + furi_thread_enumerate uint32_t FuriThreadId*, uint32_t
1814 Function + furi_thread_get_stdout_callback FuriThreadStdoutWriteCallback
1815 Function + furi_thread_is_suspended _Bool FuriThreadId
1816 Function + furi_thread_join _Bool FuriThread*
Function + furi_thread_mark_as_service void FuriThread*
1817 Function + furi_thread_resume void FuriThreadId
1818 Function + furi_thread_set_appid void FuriThread*, const char*
1819 Function + furi_thread_set_callback void FuriThread*, FuriThreadCallback

View File

@@ -268,9 +268,8 @@ void furi_hal_serial_control_init(void) {
furi_hal_serial_control->handles[FuriHalSerialIdLpuart].id = FuriHalSerialIdLpuart; furi_hal_serial_control->handles[FuriHalSerialIdLpuart].id = FuriHalSerialIdLpuart;
furi_hal_serial_control->queue = furi_hal_serial_control->queue =
furi_message_queue_alloc(8, sizeof(FuriHalSerialControlMessage)); furi_message_queue_alloc(8, sizeof(FuriHalSerialControlMessage));
furi_hal_serial_control->thread = furi_hal_serial_control->thread = furi_thread_alloc_service(
furi_thread_alloc_ex("SerialControlDriver", 512, furi_hal_serial_control_thread, NULL); "SerialControlDriver", 512, furi_hal_serial_control_thread, NULL);
furi_thread_mark_as_service(furi_hal_serial_control->thread);
furi_thread_set_priority(furi_hal_serial_control->thread, FuriThreadPriorityHighest); furi_thread_set_priority(furi_hal_serial_control->thread, FuriThreadPriorityHighest);
furi_hal_serial_control->log_config_serial_id = FuriHalSerialIdMax; furi_hal_serial_control->log_config_serial_id = FuriHalSerialIdMax;
// Start control plane thread // Start control plane thread

View File

@@ -120,8 +120,7 @@ void furi_hal_usb_init(void) {
NVIC_EnableIRQ(USB_HP_IRQn); NVIC_EnableIRQ(USB_HP_IRQn);
usb.queue = furi_message_queue_alloc(1, sizeof(UsbApiEventMessage)); usb.queue = furi_message_queue_alloc(1, sizeof(UsbApiEventMessage));
usb.thread = furi_thread_alloc_ex("UsbDriver", 1024, furi_hal_usb_thread, NULL); usb.thread = furi_thread_alloc_service("UsbDriver", 1024, furi_hal_usb_thread, NULL);
furi_thread_mark_as_service(usb.thread);
furi_thread_start(usb.thread); furi_thread_start(usb.thread);
FURI_LOG_I(TAG, "Init OK"); FURI_LOG_I(TAG, "Init OK");

View File

@@ -16,7 +16,7 @@
#define configUSE_PREEMPTION 1 #define configUSE_PREEMPTION 1
#define configSUPPORT_STATIC_ALLOCATION 1 #define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configSUPPORT_DYNAMIC_ALLOCATION 0
#define configUSE_IDLE_HOOK 0 #define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0 #define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ (SystemCoreClock) #define configCPU_CLOCK_HZ (SystemCoreClock)