mirror of
https://github.com/flipperdevices/flipperzero-firmware.git
synced 2025-12-12 04:41:26 +04:00
Furi: A Lot of Fixes (#3942)
- BT Service: cleanup code - Dialog: correct release order in file browser - Rpc: rollback to pre #3881 state - Kernel: fix inverted behavior in furi_kernel_is_running - Log: properly take mutex when kernel is not running - Thread: rework tread control block scrubbing procedure, ensure that we don't do stupid things in idle task, add new priority for init task - Timer: add control queue flush method, force flush on stop - Furi: system init task now performs thread scrubbing - BleGlue: add some extra checks - FreeRTOSConfig: fix bunch of issues that were preventing configuration from being properly applied and cleanup
This commit is contained in:
@@ -33,7 +33,7 @@ bool furi_kernel_is_irq_or_masked(void) {
|
||||
}
|
||||
|
||||
bool furi_kernel_is_running(void) {
|
||||
return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING;
|
||||
return xTaskGetSchedulerState() == taskSCHEDULER_RUNNING;
|
||||
}
|
||||
|
||||
int32_t furi_kernel_lock(void) {
|
||||
@@ -129,6 +129,8 @@ uint32_t furi_kernel_get_tick_frequency(void) {
|
||||
|
||||
void furi_delay_tick(uint32_t ticks) {
|
||||
furi_check(!furi_kernel_is_irq_or_masked());
|
||||
furi_check(furi_thread_get_current_id() != xTaskGetIdleTaskHandle());
|
||||
|
||||
if(ticks == 0U) {
|
||||
taskYIELD();
|
||||
} else {
|
||||
@@ -138,6 +140,7 @@ void furi_delay_tick(uint32_t ticks) {
|
||||
|
||||
FuriStatus furi_delay_until_tick(uint32_t tick) {
|
||||
furi_check(!furi_kernel_is_irq_or_masked());
|
||||
furi_check(furi_thread_get_current_id() != xTaskGetIdleTaskHandle());
|
||||
|
||||
TickType_t tcnt, delay;
|
||||
FuriStatus stat;
|
||||
|
||||
@@ -108,10 +108,17 @@ void furi_log_puts(const char* data) {
|
||||
}
|
||||
|
||||
void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) {
|
||||
if(level <= furi_log.log_level &&
|
||||
furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk) {
|
||||
FuriString* string;
|
||||
string = furi_string_alloc();
|
||||
do {
|
||||
if(level > furi_log.log_level) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(furi_mutex_acquire(furi_log.mutex, furi_kernel_is_running() ? FuriWaitForever : 0) !=
|
||||
FuriStatusOk) {
|
||||
break;
|
||||
}
|
||||
|
||||
FuriString* string = furi_string_alloc();
|
||||
|
||||
const char* color = _FURI_LOG_CLR_RESET;
|
||||
const char* log_letter = " ";
|
||||
@@ -157,7 +164,7 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form
|
||||
furi_log_puts("\r\n");
|
||||
|
||||
furi_mutex_release(furi_log.mutex);
|
||||
}
|
||||
} while(0);
|
||||
}
|
||||
|
||||
void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "thread.h"
|
||||
#include "thread_i.h"
|
||||
#include "thread_list_i.h"
|
||||
#include "kernel.h"
|
||||
#include "message_queue.h"
|
||||
#include "memmgr.h"
|
||||
#include "memmgr_heap.h"
|
||||
#include "check.h"
|
||||
@@ -67,6 +68,8 @@ static_assert(offsetof(FuriThread, container) == 0);
|
||||
// Our idle priority should be equal to the one from FreeRTOS
|
||||
static_assert(FuriThreadPriorityIdle == tskIDLE_PRIORITY);
|
||||
|
||||
static FuriMessageQueue* furi_thread_scrub_message_queue = NULL;
|
||||
|
||||
static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size);
|
||||
static int32_t __furi_thread_stdout_flush(FuriThread* thread);
|
||||
|
||||
@@ -125,7 +128,9 @@ static void furi_thread_body(void* context) {
|
||||
|
||||
furi_thread_set_state(thread, FuriThreadStateStopping);
|
||||
|
||||
vTaskDelete(NULL);
|
||||
furi_message_queue_put(furi_thread_scrub_message_queue, &thread, FuriWaitForever);
|
||||
|
||||
vTaskSuspend(NULL);
|
||||
furi_thread_catch();
|
||||
}
|
||||
|
||||
@@ -159,6 +164,31 @@ static void furi_thread_init_common(FuriThread* thread) {
|
||||
}
|
||||
}
|
||||
|
||||
void furi_thread_init(void) {
|
||||
furi_thread_scrub_message_queue = furi_message_queue_alloc(8, sizeof(FuriThread*));
|
||||
}
|
||||
|
||||
void furi_thread_scrub(void) {
|
||||
FuriThread* thread_to_scrub = NULL;
|
||||
while(true) {
|
||||
furi_check(
|
||||
furi_message_queue_get(
|
||||
furi_thread_scrub_message_queue, &thread_to_scrub, FuriWaitForever) ==
|
||||
FuriStatusOk);
|
||||
|
||||
TaskHandle_t task = (TaskHandle_t)thread_to_scrub;
|
||||
|
||||
// Delete task: FreeRTOS will remove task from all lists where it may be
|
||||
vTaskDelete(task);
|
||||
// Sanity check: ensure that local storage is ours and clear it
|
||||
furi_check(pvTaskGetThreadLocalStoragePointer(task, 0) == thread_to_scrub);
|
||||
vTaskSetThreadLocalStoragePointer(task, 0, NULL);
|
||||
|
||||
// Deliver thread stopped callback
|
||||
furi_thread_set_state(thread_to_scrub, FuriThreadStateStopped);
|
||||
}
|
||||
}
|
||||
|
||||
FuriThread* furi_thread_alloc(void) {
|
||||
FuriThread* thread = malloc(sizeof(FuriThread));
|
||||
|
||||
@@ -358,16 +388,6 @@ void furi_thread_start(FuriThread* thread) {
|
||||
&thread->container) == (TaskHandle_t)thread);
|
||||
}
|
||||
|
||||
void furi_thread_cleanup_tcb_event(TaskHandle_t task) {
|
||||
FuriThread* thread = pvTaskGetThreadLocalStoragePointer(task, 0);
|
||||
if(thread) {
|
||||
// clear thread local storage
|
||||
vTaskSetThreadLocalStoragePointer(task, 0, NULL);
|
||||
furi_check(thread == (FuriThread*)task);
|
||||
furi_thread_set_state(thread, FuriThreadStateStopped);
|
||||
}
|
||||
}
|
||||
|
||||
bool furi_thread_join(FuriThread* thread) {
|
||||
furi_check(thread);
|
||||
// Cannot join a service thread
|
||||
|
||||
@@ -21,10 +21,10 @@ extern "C" {
|
||||
* Many of the FuriThread functions MUST ONLY be called when the thread is STOPPED.
|
||||
*/
|
||||
typedef enum {
|
||||
FuriThreadStateStopped, /**< Thread is stopped and is safe to release */
|
||||
FuriThreadStateStopping, /**< Thread is stopping */
|
||||
FuriThreadStateStarting, /**< Thread is starting */
|
||||
FuriThreadStateRunning, /**< Thread is running */
|
||||
FuriThreadStateStopped, /**< Thread is stopped and is safe to release. Event delivered from system init thread(TCB cleanup routine). It is safe to release thread instance. */
|
||||
FuriThreadStateStopping, /**< Thread is stopping. Event delivered from child thread. */
|
||||
FuriThreadStateStarting, /**< Thread is starting. Event delivered from parent(self) thread. */
|
||||
FuriThreadStateRunning, /**< Thread is running. Event delivered from child thread. */
|
||||
} FuriThreadState;
|
||||
|
||||
/**
|
||||
@@ -32,6 +32,7 @@ typedef enum {
|
||||
*/
|
||||
typedef enum {
|
||||
FuriThreadPriorityIdle = 0, /**< Idle priority */
|
||||
FuriThreadPriorityInit = 4, /**< Init System Thread Priority */
|
||||
FuriThreadPriorityLowest = 14, /**< Lowest */
|
||||
FuriThreadPriorityLow = 15, /**< Low */
|
||||
FuriThreadPriorityNormal = 16, /**< Normal, system default */
|
||||
@@ -77,13 +78,15 @@ typedef int32_t (*FuriThreadCallback)(void* context);
|
||||
typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size);
|
||||
|
||||
/**
|
||||
* @brief State change callback function pointer type.
|
||||
* @brief State change callback function pointer type.
|
||||
*
|
||||
* The function to be used as a state callback MUST follow this signature.
|
||||
* The function to be used as a state callback MUST follow this
|
||||
* signature.
|
||||
*
|
||||
* @param[in] pointer to the FuriThread instance that changed the state
|
||||
* @param[in] state identifier of the state the thread has transitioned to
|
||||
* @param[in,out] context pointer to a user-specified object
|
||||
* @param[in] thread to the FuriThread instance that changed the state
|
||||
* @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)(FuriThread* thread, FuriThreadState state, void* context);
|
||||
|
||||
|
||||
7
furi/core/thread_i.h
Normal file
7
furi/core/thread_i.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "thread.h"
|
||||
|
||||
void furi_thread_init(void);
|
||||
|
||||
void furi_thread_scrub(void);
|
||||
@@ -17,12 +17,24 @@ static_assert(offsetof(FuriTimer, container) == 0);
|
||||
|
||||
#define TIMER_DELETED_EVENT (1U << 0)
|
||||
|
||||
static void TimerCallback(TimerHandle_t hTimer) {
|
||||
static void furi_timer_callback(TimerHandle_t hTimer) {
|
||||
FuriTimer* instance = pvTimerGetTimerID(hTimer);
|
||||
furi_check(instance);
|
||||
instance->cb_func(instance->cb_context);
|
||||
}
|
||||
|
||||
static void furi_timer_flush_epilogue(void* context, uint32_t arg) {
|
||||
furi_assert(context);
|
||||
UNUSED(arg);
|
||||
|
||||
EventGroupHandle_t hEvent = context;
|
||||
|
||||
// See https://github.com/FreeRTOS/FreeRTOS-Kernel/issues/1142
|
||||
vTaskSuspendAll();
|
||||
xEventGroupSetBits(hEvent, TIMER_DELETED_EVENT);
|
||||
(void)xTaskResumeAll();
|
||||
}
|
||||
|
||||
FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) {
|
||||
furi_check((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL));
|
||||
|
||||
@@ -33,23 +45,13 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co
|
||||
|
||||
const UBaseType_t reload = (type == FuriTimerTypeOnce ? pdFALSE : pdTRUE);
|
||||
const TimerHandle_t hTimer = xTimerCreateStatic(
|
||||
NULL, portMAX_DELAY, reload, instance, TimerCallback, &instance->container);
|
||||
NULL, portMAX_DELAY, reload, instance, furi_timer_callback, &instance->container);
|
||||
|
||||
furi_check(hTimer == (TimerHandle_t)instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void furi_timer_epilogue(void* context, uint32_t arg) {
|
||||
furi_assert(context);
|
||||
UNUSED(arg);
|
||||
|
||||
EventGroupHandle_t hEvent = context;
|
||||
vTaskSuspendAll();
|
||||
xEventGroupSetBits(hEvent, TIMER_DELETED_EVENT);
|
||||
(void)xTaskResumeAll();
|
||||
}
|
||||
|
||||
void furi_timer_free(FuriTimer* instance) {
|
||||
furi_check(!furi_kernel_is_irq_or_masked());
|
||||
furi_check(instance);
|
||||
@@ -57,16 +59,21 @@ void furi_timer_free(FuriTimer* instance) {
|
||||
TimerHandle_t hTimer = (TimerHandle_t)instance;
|
||||
furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS);
|
||||
|
||||
furi_timer_flush();
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void furi_timer_flush(void) {
|
||||
StaticEventGroup_t event_container = {};
|
||||
EventGroupHandle_t hEvent = xEventGroupCreateStatic(&event_container);
|
||||
furi_check(xTimerPendFunctionCall(furi_timer_epilogue, hEvent, 0, portMAX_DELAY) == pdPASS);
|
||||
furi_check(
|
||||
xTimerPendFunctionCall(furi_timer_flush_epilogue, hEvent, 0, portMAX_DELAY) == pdPASS);
|
||||
|
||||
furi_check(
|
||||
xEventGroupWaitBits(hEvent, TIMER_DELETED_EVENT, pdFALSE, pdTRUE, portMAX_DELAY) ==
|
||||
TIMER_DELETED_EVENT);
|
||||
vEventGroupDelete(hEvent);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {
|
||||
@@ -112,6 +119,8 @@ FuriStatus furi_timer_stop(FuriTimer* instance) {
|
||||
|
||||
furi_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS);
|
||||
|
||||
furi_timer_flush();
|
||||
|
||||
return FuriStatusOk;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,12 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co
|
||||
*/
|
||||
void furi_timer_free(FuriTimer* instance);
|
||||
|
||||
/** Flush timer task control message queue
|
||||
*
|
||||
* Ensures that all commands before this point was processed.
|
||||
*/
|
||||
void furi_timer_flush(void);
|
||||
|
||||
/** Start timer
|
||||
*
|
||||
* @warning This is asynchronous call, real operation will happen as soon as
|
||||
@@ -61,8 +67,7 @@ FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks);
|
||||
|
||||
/** Stop timer
|
||||
*
|
||||
* @warning This is asynchronous call, real operation will happen as soon as
|
||||
* timer service process this request.
|
||||
* @warning This is synchronous call that will be blocked till timer queue processed.
|
||||
*
|
||||
* @param instance The pointer to FuriTimer instance
|
||||
*
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "furi.h"
|
||||
|
||||
#include "core/thread_i.h"
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include <queue.h>
|
||||
|
||||
@@ -7,6 +9,7 @@ void furi_init(void) {
|
||||
furi_check(!furi_kernel_is_irq_or_masked());
|
||||
furi_check(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED);
|
||||
|
||||
furi_thread_init();
|
||||
furi_log_init();
|
||||
furi_record_init();
|
||||
}
|
||||
@@ -18,3 +21,7 @@ void furi_run(void) {
|
||||
/* Start the kernel scheduler */
|
||||
vTaskStartScheduler();
|
||||
}
|
||||
|
||||
void furi_background(void) {
|
||||
furi_thread_scrub();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ void furi_init(void);
|
||||
|
||||
void furi_run(void);
|
||||
|
||||
void furi_background(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user