mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-23 05:14:16 +03: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:
parent
03196fa110
commit
20c4121f25
@ -1,11 +1,6 @@
|
||||
#include <core/check.h>
|
||||
#include <core/record.h>
|
||||
#include <furi.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
|
||||
#include <rpc/rpc.h>
|
||||
#include <rpc/rpc_i.h>
|
||||
#include <cli/cli.h>
|
||||
@ -40,8 +35,8 @@ static uint32_t command_id = 0;
|
||||
typedef struct {
|
||||
RpcSession* session;
|
||||
FuriStreamBuffer* output_stream;
|
||||
SemaphoreHandle_t close_session_semaphore;
|
||||
SemaphoreHandle_t terminate_semaphore;
|
||||
FuriSemaphore* close_session_semaphore;
|
||||
FuriSemaphore* terminate_semaphore;
|
||||
uint32_t timeout;
|
||||
} RpcSessionContext;
|
||||
|
||||
@ -96,8 +91,8 @@ static void test_rpc_setup(void) {
|
||||
|
||||
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[0].close_session_semaphore = xSemaphoreCreateBinary();
|
||||
rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary();
|
||||
rpc_session[0].close_session_semaphore = furi_semaphore_alloc(1, 0);
|
||||
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_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_set_send_bytes_callback(rpc_session[1].session, output_bytes_callback);
|
||||
rpc_session[1].close_session_semaphore = xSemaphoreCreateBinary();
|
||||
rpc_session[1].terminate_semaphore = xSemaphoreCreateBinary();
|
||||
rpc_session[1].close_session_semaphore = furi_semaphore_alloc(1, 0);
|
||||
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_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) {
|
||||
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);
|
||||
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_stream_buffer_free(rpc_session[0].output_stream);
|
||||
vSemaphoreDelete(rpc_session[0].close_session_semaphore);
|
||||
vSemaphoreDelete(rpc_session[0].terminate_semaphore);
|
||||
furi_semaphore_free(rpc_session[0].close_session_semaphore);
|
||||
furi_semaphore_free(rpc_session[0].terminate_semaphore);
|
||||
++command_id;
|
||||
rpc_session[0].output_stream = 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) {
|
||||
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);
|
||||
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);
|
||||
vSemaphoreDelete(rpc_session[1].close_session_semaphore);
|
||||
vSemaphoreDelete(rpc_session[1].terminate_semaphore);
|
||||
furi_semaphore_free(rpc_session[1].close_session_semaphore);
|
||||
furi_semaphore_free(rpc_session[1].terminate_semaphore);
|
||||
++command_id;
|
||||
rpc_session[1].output_stream = NULL;
|
||||
rpc_session[1].close_session_semaphore = NULL;
|
||||
@ -204,14 +203,14 @@ static void test_rpc_session_close_callback(void* context) {
|
||||
furi_check(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) {
|
||||
furi_check(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) {
|
||||
@ -1645,7 +1644,7 @@ static void test_rpc_feed_rubbish_run(
|
||||
|
||||
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_send_rubbish(rpc_session[0].session, pattern, pattern_size, size);
|
||||
test_rpc_encode_and_feed(input_after, 0);
|
||||
|
@ -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(vQueueDelete, void, (QueueHandle_t)),
|
||||
API_METHOD(
|
||||
xQueueGenericCreate,
|
||||
xQueueGenericCreateStatic,
|
||||
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(
|
||||
xQueueGenericSend,
|
||||
BaseType_t,
|
||||
|
@ -435,6 +435,8 @@ int32_t bt_srv(void* p) {
|
||||
FURI_LOG_W(TAG, "Skipping start in special boot mode");
|
||||
ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT);
|
||||
furi_record_create(RECORD_BT, bt);
|
||||
|
||||
furi_thread_suspend(furi_thread_get_current_id());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -441,6 +441,8 @@ int32_t desktop_srv(void* p) {
|
||||
|
||||
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
|
||||
FURI_LOG_W(TAG, "Skipping start in special boot mode");
|
||||
|
||||
furi_thread_suspend(furi_thread_get_current_id());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include <furi.h>
|
||||
#include <FreeRTOS.h>
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
#include "../desktop_i.h"
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include <furi.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <FreeRTOS.h>
|
||||
#include <projdefs.h>
|
||||
#include <input/input.h>
|
||||
#include <gui/canvas.h>
|
||||
|
@ -148,6 +148,8 @@ int32_t dolphin_srv(void* p) {
|
||||
|
||||
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
|
||||
FURI_LOG_W(TAG, "Skipping start in special boot mode");
|
||||
|
||||
furi_thread_suspend(furi_thread_get_current_id());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -198,6 +198,8 @@ int32_t power_srv(void* p) {
|
||||
|
||||
if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
|
||||
FURI_LOG_W(TAG, "Skipping start in special boot mode");
|
||||
|
||||
furi_thread_suspend(furi_thread_get_current_id());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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_keys_storage.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_thread_alloc,FuriThread*,
|
||||
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_enable_heap_trace,void,FuriThread*
|
||||
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_is_suspended,_Bool,FuriThreadId
|
||||
Function,+,furi_thread_join,_Bool,FuriThread*
|
||||
Function,+,furi_thread_mark_as_service,void,FuriThread*
|
||||
Function,+,furi_thread_resume,void,FuriThreadId
|
||||
Function,+,furi_thread_set_appid,void,"FuriThread*, const char*"
|
||||
Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
|
||||
|
|
@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,63.0,,
|
||||
Version,+,64.0,,
|
||||
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_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_thread_alloc,FuriThread*,
|
||||
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_enable_heap_trace,void,FuriThread*
|
||||
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_is_suspended,_Bool,FuriThreadId
|
||||
Function,+,furi_thread_join,_Bool,FuriThread*
|
||||
Function,+,furi_thread_mark_as_service,void,FuriThread*
|
||||
Function,+,furi_thread_resume,void,FuriThreadId
|
||||
Function,+,furi_thread_set_appid,void,"FuriThread*, const char*"
|
||||
Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
|
||||
|
|
@ -268,9 +268,8 @@ void furi_hal_serial_control_init(void) {
|
||||
furi_hal_serial_control->handles[FuriHalSerialIdLpuart].id = FuriHalSerialIdLpuart;
|
||||
furi_hal_serial_control->queue =
|
||||
furi_message_queue_alloc(8, sizeof(FuriHalSerialControlMessage));
|
||||
furi_hal_serial_control->thread =
|
||||
furi_thread_alloc_ex("SerialControlDriver", 512, furi_hal_serial_control_thread, NULL);
|
||||
furi_thread_mark_as_service(furi_hal_serial_control->thread);
|
||||
furi_hal_serial_control->thread = furi_thread_alloc_service(
|
||||
"SerialControlDriver", 512, furi_hal_serial_control_thread, NULL);
|
||||
furi_thread_set_priority(furi_hal_serial_control->thread, FuriThreadPriorityHighest);
|
||||
furi_hal_serial_control->log_config_serial_id = FuriHalSerialIdMax;
|
||||
// Start control plane thread
|
||||
|
@ -120,8 +120,7 @@ void furi_hal_usb_init(void) {
|
||||
NVIC_EnableIRQ(USB_HP_IRQn);
|
||||
|
||||
usb.queue = furi_message_queue_alloc(1, sizeof(UsbApiEventMessage));
|
||||
usb.thread = furi_thread_alloc_ex("UsbDriver", 1024, furi_hal_usb_thread, NULL);
|
||||
furi_thread_mark_as_service(usb.thread);
|
||||
usb.thread = furi_thread_alloc_service("UsbDriver", 1024, furi_hal_usb_thread, NULL);
|
||||
furi_thread_start(usb.thread);
|
||||
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
#define configUSE_PREEMPTION 1
|
||||
#define configSUPPORT_STATIC_ALLOCATION 1
|
||||
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||
#define configSUPPORT_DYNAMIC_ALLOCATION 0
|
||||
#define configUSE_IDLE_HOOK 0
|
||||
#define configUSE_TICK_HOOK 0
|
||||
#define configCPU_CLOCK_HZ (SystemCoreClock)
|
||||
|
Loading…
Reference in New Issue
Block a user