mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge remote-tracking branch 'ofw/dev' into mntm-dev
This commit is contained in:
@@ -418,6 +418,18 @@ void furi_event_loop_unsubscribe(FuriEventLoop* instance, FuriEventLoopObject* o
|
||||
FURI_CRITICAL_EXIT();
|
||||
}
|
||||
|
||||
bool furi_event_loop_is_subscribed(FuriEventLoop* instance, FuriEventLoopObject* object) {
|
||||
furi_check(instance);
|
||||
furi_check(instance->thread_id == furi_thread_get_current_id());
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
||||
FuriEventLoopItem* const* item = FuriEventLoopTree_cget(instance->tree, object);
|
||||
bool result = !!item;
|
||||
|
||||
FURI_CRITICAL_EXIT();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Private Event Loop Item functions
|
||||
*/
|
||||
|
||||
@@ -289,6 +289,23 @@ void furi_event_loop_subscribe_mutex(
|
||||
*/
|
||||
void furi_event_loop_unsubscribe(FuriEventLoop* instance, FuriEventLoopObject* object);
|
||||
|
||||
/**
|
||||
* @brief Checks if the loop is subscribed to an object of any kind
|
||||
*
|
||||
* @param instance Event Loop instance
|
||||
* @param object Object to check
|
||||
*/
|
||||
bool furi_event_loop_is_subscribed(FuriEventLoop* instance, FuriEventLoopObject* object);
|
||||
|
||||
/**
|
||||
* @brief Convenience function for `if(is_subscribed()) unsubscribe()`
|
||||
*/
|
||||
static inline void
|
||||
furi_event_loop_maybe_unsubscribe(FuriEventLoop* instance, FuriEventLoopObject* object) {
|
||||
if(furi_event_loop_is_subscribed(instance, object))
|
||||
furi_event_loop_unsubscribe(instance, object);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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,7 +1,8 @@
|
||||
#include "thread.h"
|
||||
#include "thread_i.h"
|
||||
#include "thread_list_i.h"
|
||||
#include "timer.h"
|
||||
#include "kernel.h"
|
||||
#include "message_queue.h"
|
||||
#include "memmgr.h"
|
||||
#include "memmgr_heap.h"
|
||||
#include "check.h"
|
||||
@@ -69,6 +70,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);
|
||||
|
||||
@@ -127,7 +130,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();
|
||||
}
|
||||
|
||||
@@ -161,6 +166,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));
|
||||
|
||||
@@ -360,16 +390,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);
|
||||
@@ -24,7 +24,7 @@ const char* furi_timer_get_current_name(void) {
|
||||
return current_timer_name;
|
||||
}
|
||||
|
||||
static void TimerCallback(TimerHandle_t hTimer) {
|
||||
static void furi_timer_callback(TimerHandle_t hTimer) {
|
||||
FuriTimer* instance = pvTimerGetTimerID(hTimer);
|
||||
furi_check(instance);
|
||||
current_timer_name = pcTimerGetName(hTimer);
|
||||
@@ -32,6 +32,18 @@ static void TimerCallback(TimerHandle_t hTimer) {
|
||||
current_timer_name = NULL;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
@@ -45,23 +57,13 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co
|
||||
|
||||
const UBaseType_t reload = (type == FuriTimerTypeOnce ? pdFALSE : pdTRUE);
|
||||
const TimerHandle_t hTimer = xTimerCreateStatic(
|
||||
name, portMAX_DELAY, reload, instance, TimerCallback, &instance->container);
|
||||
name, 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);
|
||||
@@ -69,16 +71,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) {
|
||||
@@ -124,6 +131,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