mirror of
https://github.com/flipperdevices/flipperzero-firmware.git
synced 2025-12-12 04:41:26 +04:00
FurEventLoop: add support for FuriEventFlag, simplify API (#3958)
* Core: event_flag, removing duplicate code * event_loop: add support furi_event_flags * Examples: add missing free in event loop examples * Furi: fix event flag * Sync api symbols * Unit_test: evet_loop_event_flags * Fix multiple waiting list elements handling * Unit_test: add event_loop_event_flag test * FURI: event_loop add restrictions * Fix multiple waiting lists items for good * Improve FuriEventLoop unit tests * Abolish callback return value * Remove return value from callback signature * Use bool level value instead of int32_t * Add unit tests for FuriStreamBuffer * Add unit tests for FuriSemaphore * Speed up test execution * Improve docs * Add a stub for furi os-level primitives * Add more checks for edge cases * Allow event loop notification from ISR * Bump api version Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com> Co-authored-by: Georgii Surkov <georgii.surkov@outlook.com> Co-authored-by: Georgii Surkov <37121527+gsurkov@users.noreply.github.com>
This commit is contained in:
@@ -82,7 +82,7 @@ static void view_port_input_callback(InputEvent* input_event, void* context) {
|
|||||||
furi_message_queue_put(app->input_queue, input_event, 0);
|
furi_message_queue_put(app->input_queue, input_event, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool input_queue_callback(FuriEventLoopObject* object, void* context) {
|
static void input_queue_callback(FuriEventLoopObject* object, void* context) {
|
||||||
FuriMessageQueue* queue = object;
|
FuriMessageQueue* queue = object;
|
||||||
EventLoopBlinkTestApp* app = context;
|
EventLoopBlinkTestApp* app = context;
|
||||||
|
|
||||||
@@ -107,8 +107,6 @@ static bool input_queue_callback(FuriEventLoopObject* object, void* context) {
|
|||||||
furi_event_loop_stop(app->event_loop);
|
furi_event_loop_stop(app->event_loop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blink_timer_callback(void* context) {
|
static void blink_timer_callback(void* context) {
|
||||||
|
|||||||
@@ -1,205 +0,0 @@
|
|||||||
#include "../test.h"
|
|
||||||
#include <furi.h>
|
|
||||||
#include <furi_hal.h>
|
|
||||||
|
|
||||||
#include <FreeRTOS.h>
|
|
||||||
#include <task.h>
|
|
||||||
|
|
||||||
#define TAG "TestFuriEventLoop"
|
|
||||||
|
|
||||||
#define EVENT_LOOP_EVENT_COUNT (256u)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FuriMessageQueue* mq;
|
|
||||||
|
|
||||||
FuriEventLoop* producer_event_loop;
|
|
||||||
uint32_t producer_counter;
|
|
||||||
|
|
||||||
FuriEventLoop* consumer_event_loop;
|
|
||||||
uint32_t consumer_counter;
|
|
||||||
} TestFuriData;
|
|
||||||
|
|
||||||
bool test_furi_event_loop_producer_mq_callback(FuriEventLoopObject* object, void* context) {
|
|
||||||
furi_check(context);
|
|
||||||
|
|
||||||
TestFuriData* data = context;
|
|
||||||
furi_check(data->mq == object, "Invalid queue");
|
|
||||||
|
|
||||||
FURI_LOG_I(
|
|
||||||
TAG, "producer_mq_callback: %lu %lu", data->producer_counter, data->consumer_counter);
|
|
||||||
|
|
||||||
if(data->producer_counter == EVENT_LOOP_EVENT_COUNT / 2) {
|
|
||||||
furi_event_loop_unsubscribe(data->producer_event_loop, data->mq);
|
|
||||||
furi_event_loop_subscribe_message_queue(
|
|
||||||
data->producer_event_loop,
|
|
||||||
data->mq,
|
|
||||||
FuriEventLoopEventOut,
|
|
||||||
test_furi_event_loop_producer_mq_callback,
|
|
||||||
data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(data->producer_counter == EVENT_LOOP_EVENT_COUNT) {
|
|
||||||
furi_event_loop_stop(data->producer_event_loop);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->producer_counter++;
|
|
||||||
furi_check(
|
|
||||||
furi_message_queue_put(data->mq, &data->producer_counter, 0) == FuriStatusOk,
|
|
||||||
"furi_message_queue_put failed");
|
|
||||||
furi_delay_us(furi_hal_random_get() % 1000);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t test_furi_event_loop_producer(void* p) {
|
|
||||||
furi_check(p);
|
|
||||||
|
|
||||||
TestFuriData* data = p;
|
|
||||||
|
|
||||||
FURI_LOG_I(TAG, "producer start 1st run");
|
|
||||||
|
|
||||||
data->producer_event_loop = furi_event_loop_alloc();
|
|
||||||
furi_event_loop_subscribe_message_queue(
|
|
||||||
data->producer_event_loop,
|
|
||||||
data->mq,
|
|
||||||
FuriEventLoopEventOut,
|
|
||||||
test_furi_event_loop_producer_mq_callback,
|
|
||||||
data);
|
|
||||||
|
|
||||||
furi_event_loop_run(data->producer_event_loop);
|
|
||||||
|
|
||||||
// 2 EventLoop index, 0xFFFFFFFF - all possible flags, emulate uncleared flags
|
|
||||||
xTaskNotifyIndexed(xTaskGetCurrentTaskHandle(), 2, 0xFFFFFFFF, eSetBits);
|
|
||||||
|
|
||||||
furi_event_loop_unsubscribe(data->producer_event_loop, data->mq);
|
|
||||||
furi_event_loop_free(data->producer_event_loop);
|
|
||||||
|
|
||||||
FURI_LOG_I(TAG, "producer start 2nd run");
|
|
||||||
|
|
||||||
data->producer_counter = 0;
|
|
||||||
data->producer_event_loop = furi_event_loop_alloc();
|
|
||||||
|
|
||||||
furi_event_loop_subscribe_message_queue(
|
|
||||||
data->producer_event_loop,
|
|
||||||
data->mq,
|
|
||||||
FuriEventLoopEventOut,
|
|
||||||
test_furi_event_loop_producer_mq_callback,
|
|
||||||
data);
|
|
||||||
|
|
||||||
furi_event_loop_run(data->producer_event_loop);
|
|
||||||
|
|
||||||
furi_event_loop_unsubscribe(data->producer_event_loop, data->mq);
|
|
||||||
furi_event_loop_free(data->producer_event_loop);
|
|
||||||
|
|
||||||
FURI_LOG_I(TAG, "producer end");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool test_furi_event_loop_consumer_mq_callback(FuriEventLoopObject* object, void* context) {
|
|
||||||
furi_check(context);
|
|
||||||
|
|
||||||
TestFuriData* data = context;
|
|
||||||
furi_check(data->mq == object);
|
|
||||||
|
|
||||||
furi_delay_us(furi_hal_random_get() % 1000);
|
|
||||||
furi_check(furi_message_queue_get(data->mq, &data->consumer_counter, 0) == FuriStatusOk);
|
|
||||||
|
|
||||||
FURI_LOG_I(
|
|
||||||
TAG, "consumer_mq_callback: %lu %lu", data->producer_counter, data->consumer_counter);
|
|
||||||
|
|
||||||
if(data->consumer_counter == EVENT_LOOP_EVENT_COUNT / 2) {
|
|
||||||
furi_event_loop_unsubscribe(data->consumer_event_loop, data->mq);
|
|
||||||
furi_event_loop_subscribe_message_queue(
|
|
||||||
data->consumer_event_loop,
|
|
||||||
data->mq,
|
|
||||||
FuriEventLoopEventIn,
|
|
||||||
test_furi_event_loop_consumer_mq_callback,
|
|
||||||
data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(data->consumer_counter == EVENT_LOOP_EVENT_COUNT) {
|
|
||||||
furi_event_loop_stop(data->consumer_event_loop);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t test_furi_event_loop_consumer(void* p) {
|
|
||||||
furi_check(p);
|
|
||||||
|
|
||||||
TestFuriData* data = p;
|
|
||||||
|
|
||||||
FURI_LOG_I(TAG, "consumer start 1st run");
|
|
||||||
|
|
||||||
data->consumer_event_loop = furi_event_loop_alloc();
|
|
||||||
furi_event_loop_subscribe_message_queue(
|
|
||||||
data->consumer_event_loop,
|
|
||||||
data->mq,
|
|
||||||
FuriEventLoopEventIn,
|
|
||||||
test_furi_event_loop_consumer_mq_callback,
|
|
||||||
data);
|
|
||||||
|
|
||||||
furi_event_loop_run(data->consumer_event_loop);
|
|
||||||
|
|
||||||
// 2 EventLoop index, 0xFFFFFFFF - all possible flags, emulate uncleared flags
|
|
||||||
xTaskNotifyIndexed(xTaskGetCurrentTaskHandle(), 2, 0xFFFFFFFF, eSetBits);
|
|
||||||
|
|
||||||
furi_event_loop_unsubscribe(data->consumer_event_loop, data->mq);
|
|
||||||
furi_event_loop_free(data->consumer_event_loop);
|
|
||||||
|
|
||||||
FURI_LOG_I(TAG, "consumer start 2nd run");
|
|
||||||
|
|
||||||
data->consumer_counter = 0;
|
|
||||||
data->consumer_event_loop = furi_event_loop_alloc();
|
|
||||||
furi_event_loop_subscribe_message_queue(
|
|
||||||
data->consumer_event_loop,
|
|
||||||
data->mq,
|
|
||||||
FuriEventLoopEventIn,
|
|
||||||
test_furi_event_loop_consumer_mq_callback,
|
|
||||||
data);
|
|
||||||
|
|
||||||
furi_event_loop_run(data->consumer_event_loop);
|
|
||||||
|
|
||||||
furi_event_loop_unsubscribe(data->consumer_event_loop, data->mq);
|
|
||||||
furi_event_loop_free(data->consumer_event_loop);
|
|
||||||
|
|
||||||
FURI_LOG_I(TAG, "consumer end");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_furi_event_loop(void) {
|
|
||||||
TestFuriData data = {};
|
|
||||||
|
|
||||||
data.mq = furi_message_queue_alloc(16, sizeof(uint32_t));
|
|
||||||
|
|
||||||
FuriThread* producer_thread = furi_thread_alloc();
|
|
||||||
furi_thread_set_name(producer_thread, "producer_thread");
|
|
||||||
furi_thread_set_stack_size(producer_thread, 1 * 1024);
|
|
||||||
furi_thread_set_callback(producer_thread, test_furi_event_loop_producer);
|
|
||||||
furi_thread_set_context(producer_thread, &data);
|
|
||||||
furi_thread_start(producer_thread);
|
|
||||||
|
|
||||||
FuriThread* consumer_thread = furi_thread_alloc();
|
|
||||||
furi_thread_set_name(consumer_thread, "consumer_thread");
|
|
||||||
furi_thread_set_stack_size(consumer_thread, 1 * 1024);
|
|
||||||
furi_thread_set_callback(consumer_thread, test_furi_event_loop_consumer);
|
|
||||||
furi_thread_set_context(consumer_thread, &data);
|
|
||||||
furi_thread_start(consumer_thread);
|
|
||||||
|
|
||||||
// Wait for thread to complete their tasks
|
|
||||||
furi_thread_join(producer_thread);
|
|
||||||
furi_thread_join(consumer_thread);
|
|
||||||
|
|
||||||
// The test itself
|
|
||||||
mu_assert_int_eq(data.producer_counter, data.consumer_counter);
|
|
||||||
mu_assert_int_eq(data.producer_counter, EVENT_LOOP_EVENT_COUNT);
|
|
||||||
|
|
||||||
// Release memory
|
|
||||||
furi_thread_free(consumer_thread);
|
|
||||||
furi_thread_free(producer_thread);
|
|
||||||
furi_message_queue_free(data.mq);
|
|
||||||
}
|
|
||||||
490
applications/debug/unit_tests/tests/furi/furi_event_loop_test.c
Normal file
490
applications/debug/unit_tests/tests/furi/furi_event_loop_test.c
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
#include "../test.h"
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <task.h>
|
||||||
|
|
||||||
|
#define TAG "TestFuriEventLoop"
|
||||||
|
|
||||||
|
#define MESSAGE_COUNT (256UL)
|
||||||
|
#define EVENT_FLAG_COUNT (23UL)
|
||||||
|
#define PRIMITIVE_COUNT (4UL)
|
||||||
|
#define RUN_COUNT (2UL)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FuriEventLoop* event_loop;
|
||||||
|
uint32_t message_queue_count;
|
||||||
|
uint32_t stream_buffer_count;
|
||||||
|
uint32_t event_flag_count;
|
||||||
|
uint32_t semaphore_count;
|
||||||
|
uint32_t primitives_tested;
|
||||||
|
} TestFuriEventLoopThread;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FuriMessageQueue* message_queue;
|
||||||
|
FuriStreamBuffer* stream_buffer;
|
||||||
|
FuriEventFlag* event_flag;
|
||||||
|
FuriSemaphore* semaphore;
|
||||||
|
|
||||||
|
TestFuriEventLoopThread producer;
|
||||||
|
TestFuriEventLoopThread consumer;
|
||||||
|
} TestFuriEventLoopData;
|
||||||
|
|
||||||
|
static void test_furi_event_loop_pending_callback(void* context) {
|
||||||
|
furi_check(context);
|
||||||
|
|
||||||
|
TestFuriEventLoopThread* test_thread = context;
|
||||||
|
furi_check(test_thread->primitives_tested < PRIMITIVE_COUNT);
|
||||||
|
|
||||||
|
test_thread->primitives_tested++;
|
||||||
|
FURI_LOG_I(TAG, "primitives tested: %lu", test_thread->primitives_tested);
|
||||||
|
|
||||||
|
if(test_thread->primitives_tested == PRIMITIVE_COUNT) {
|
||||||
|
furi_event_loop_stop(test_thread->event_loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_furi_event_loop_thread_init(TestFuriEventLoopThread* test_thread) {
|
||||||
|
memset(test_thread, 0, sizeof(TestFuriEventLoopThread));
|
||||||
|
test_thread->event_loop = furi_event_loop_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_furi_event_loop_thread_run_and_cleanup(TestFuriEventLoopThread* test_thread) {
|
||||||
|
furi_event_loop_run(test_thread->event_loop);
|
||||||
|
// 2 EventLoop index, 0xFFFFFFFF - all possible flags, emulate uncleared flags
|
||||||
|
xTaskNotifyIndexed(xTaskGetCurrentTaskHandle(), 2, 0xFFFFFFFF, eSetBits);
|
||||||
|
furi_event_loop_free(test_thread->event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_furi_event_loop_producer_message_queue_callback(
|
||||||
|
FuriEventLoopObject* object,
|
||||||
|
void* context) {
|
||||||
|
furi_check(context);
|
||||||
|
|
||||||
|
TestFuriEventLoopData* data = context;
|
||||||
|
furi_check(data->message_queue == object);
|
||||||
|
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"producer MessageQueue: %lu %lu",
|
||||||
|
data->producer.message_queue_count,
|
||||||
|
data->consumer.message_queue_count);
|
||||||
|
|
||||||
|
if(data->producer.message_queue_count == MESSAGE_COUNT / 2) {
|
||||||
|
furi_event_loop_unsubscribe(data->producer.event_loop, data->message_queue);
|
||||||
|
furi_event_loop_subscribe_message_queue(
|
||||||
|
data->producer.event_loop,
|
||||||
|
data->message_queue,
|
||||||
|
FuriEventLoopEventOut,
|
||||||
|
test_furi_event_loop_producer_message_queue_callback,
|
||||||
|
data);
|
||||||
|
|
||||||
|
} else if(data->producer.message_queue_count == MESSAGE_COUNT) {
|
||||||
|
furi_event_loop_unsubscribe(data->producer.event_loop, data->message_queue);
|
||||||
|
furi_event_loop_pend_callback(
|
||||||
|
data->producer.event_loop, test_furi_event_loop_pending_callback, &data->producer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->producer.message_queue_count++;
|
||||||
|
|
||||||
|
furi_check(
|
||||||
|
furi_message_queue_put(data->message_queue, &data->producer.message_queue_count, 0) ==
|
||||||
|
FuriStatusOk);
|
||||||
|
|
||||||
|
furi_delay_us(furi_hal_random_get() % 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_furi_event_loop_producer_stream_buffer_callback(
|
||||||
|
FuriEventLoopObject* object,
|
||||||
|
void* context) {
|
||||||
|
furi_check(context);
|
||||||
|
|
||||||
|
TestFuriEventLoopData* data = context;
|
||||||
|
furi_check(data->stream_buffer == object);
|
||||||
|
|
||||||
|
TestFuriEventLoopThread* producer = &data->producer;
|
||||||
|
TestFuriEventLoopThread* consumer = &data->consumer;
|
||||||
|
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"producer StreamBuffer: %lu %lu",
|
||||||
|
producer->stream_buffer_count,
|
||||||
|
consumer->stream_buffer_count);
|
||||||
|
|
||||||
|
if(producer->stream_buffer_count == MESSAGE_COUNT / 2) {
|
||||||
|
furi_event_loop_unsubscribe(producer->event_loop, data->stream_buffer);
|
||||||
|
furi_event_loop_subscribe_stream_buffer(
|
||||||
|
producer->event_loop,
|
||||||
|
data->stream_buffer,
|
||||||
|
FuriEventLoopEventOut,
|
||||||
|
test_furi_event_loop_producer_stream_buffer_callback,
|
||||||
|
data);
|
||||||
|
|
||||||
|
} else if(producer->stream_buffer_count == MESSAGE_COUNT) {
|
||||||
|
furi_event_loop_unsubscribe(producer->event_loop, data->stream_buffer);
|
||||||
|
furi_event_loop_pend_callback(
|
||||||
|
producer->event_loop, test_furi_event_loop_pending_callback, producer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
producer->stream_buffer_count++;
|
||||||
|
|
||||||
|
furi_check(
|
||||||
|
furi_stream_buffer_send(
|
||||||
|
data->stream_buffer, &producer->stream_buffer_count, sizeof(uint32_t), 0) ==
|
||||||
|
sizeof(uint32_t));
|
||||||
|
|
||||||
|
furi_delay_us(furi_hal_random_get() % 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_furi_event_loop_producer_event_flag_callback(FuriEventLoopObject* object, void* context) {
|
||||||
|
furi_check(context);
|
||||||
|
|
||||||
|
TestFuriEventLoopData* data = context;
|
||||||
|
furi_check(data->event_flag == object);
|
||||||
|
|
||||||
|
const uint32_t producer_flags = (1UL << data->producer.event_flag_count);
|
||||||
|
const uint32_t consumer_flags = (1UL << data->consumer.event_flag_count);
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "producer EventFlag: 0x%06lX 0x%06lX", producer_flags, consumer_flags);
|
||||||
|
|
||||||
|
furi_check(furi_event_flag_set(data->event_flag, producer_flags) & producer_flags);
|
||||||
|
|
||||||
|
if(data->producer.event_flag_count == EVENT_FLAG_COUNT / 2) {
|
||||||
|
furi_event_loop_unsubscribe(data->producer.event_loop, data->event_flag);
|
||||||
|
furi_event_loop_subscribe_event_flag(
|
||||||
|
data->producer.event_loop,
|
||||||
|
data->event_flag,
|
||||||
|
FuriEventLoopEventOut,
|
||||||
|
test_furi_event_loop_producer_event_flag_callback,
|
||||||
|
data);
|
||||||
|
|
||||||
|
} else if(data->producer.event_flag_count == EVENT_FLAG_COUNT) {
|
||||||
|
furi_event_loop_unsubscribe(data->producer.event_loop, data->event_flag);
|
||||||
|
furi_event_loop_pend_callback(
|
||||||
|
data->producer.event_loop, test_furi_event_loop_pending_callback, &data->producer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->producer.event_flag_count++;
|
||||||
|
|
||||||
|
furi_delay_us(furi_hal_random_get() % 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_furi_event_loop_producer_semaphore_callback(FuriEventLoopObject* object, void* context) {
|
||||||
|
furi_check(context);
|
||||||
|
|
||||||
|
TestFuriEventLoopData* data = context;
|
||||||
|
furi_check(data->semaphore == object);
|
||||||
|
|
||||||
|
TestFuriEventLoopThread* producer = &data->producer;
|
||||||
|
TestFuriEventLoopThread* consumer = &data->consumer;
|
||||||
|
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG, "producer Semaphore: %lu %lu", producer->semaphore_count, consumer->semaphore_count);
|
||||||
|
furi_check(furi_semaphore_release(data->semaphore) == FuriStatusOk);
|
||||||
|
|
||||||
|
if(producer->semaphore_count == MESSAGE_COUNT / 2) {
|
||||||
|
furi_event_loop_unsubscribe(producer->event_loop, data->semaphore);
|
||||||
|
furi_event_loop_subscribe_semaphore(
|
||||||
|
producer->event_loop,
|
||||||
|
data->semaphore,
|
||||||
|
FuriEventLoopEventOut,
|
||||||
|
test_furi_event_loop_producer_semaphore_callback,
|
||||||
|
data);
|
||||||
|
|
||||||
|
} else if(producer->semaphore_count == MESSAGE_COUNT) {
|
||||||
|
furi_event_loop_unsubscribe(producer->event_loop, data->semaphore);
|
||||||
|
furi_event_loop_pend_callback(
|
||||||
|
producer->event_loop, test_furi_event_loop_pending_callback, producer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->producer.semaphore_count++;
|
||||||
|
|
||||||
|
furi_delay_us(furi_hal_random_get() % 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t test_furi_event_loop_producer(void* p) {
|
||||||
|
furi_check(p);
|
||||||
|
|
||||||
|
TestFuriEventLoopData* data = p;
|
||||||
|
TestFuriEventLoopThread* producer = &data->producer;
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < RUN_COUNT; ++i) {
|
||||||
|
FURI_LOG_I(TAG, "producer start run %lu", i);
|
||||||
|
|
||||||
|
test_furi_event_loop_thread_init(producer);
|
||||||
|
|
||||||
|
furi_event_loop_subscribe_message_queue(
|
||||||
|
producer->event_loop,
|
||||||
|
data->message_queue,
|
||||||
|
FuriEventLoopEventOut,
|
||||||
|
test_furi_event_loop_producer_message_queue_callback,
|
||||||
|
data);
|
||||||
|
furi_event_loop_subscribe_stream_buffer(
|
||||||
|
producer->event_loop,
|
||||||
|
data->stream_buffer,
|
||||||
|
FuriEventLoopEventOut,
|
||||||
|
test_furi_event_loop_producer_stream_buffer_callback,
|
||||||
|
data);
|
||||||
|
furi_event_loop_subscribe_event_flag(
|
||||||
|
producer->event_loop,
|
||||||
|
data->event_flag,
|
||||||
|
FuriEventLoopEventOut,
|
||||||
|
test_furi_event_loop_producer_event_flag_callback,
|
||||||
|
data);
|
||||||
|
furi_event_loop_subscribe_semaphore(
|
||||||
|
producer->event_loop,
|
||||||
|
data->semaphore,
|
||||||
|
FuriEventLoopEventOut,
|
||||||
|
test_furi_event_loop_producer_semaphore_callback,
|
||||||
|
data);
|
||||||
|
|
||||||
|
test_furi_event_loop_thread_run_and_cleanup(producer);
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "producer end");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_furi_event_loop_consumer_message_queue_callback(
|
||||||
|
FuriEventLoopObject* object,
|
||||||
|
void* context) {
|
||||||
|
furi_check(context);
|
||||||
|
|
||||||
|
TestFuriEventLoopData* data = context;
|
||||||
|
furi_check(data->message_queue == object);
|
||||||
|
|
||||||
|
furi_delay_us(furi_hal_random_get() % 100);
|
||||||
|
|
||||||
|
furi_check(
|
||||||
|
furi_message_queue_get(data->message_queue, &data->consumer.message_queue_count, 0) ==
|
||||||
|
FuriStatusOk);
|
||||||
|
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"consumer MessageQueue: %lu %lu",
|
||||||
|
data->producer.message_queue_count,
|
||||||
|
data->consumer.message_queue_count);
|
||||||
|
|
||||||
|
if(data->consumer.message_queue_count == MESSAGE_COUNT / 2) {
|
||||||
|
furi_event_loop_unsubscribe(data->consumer.event_loop, data->message_queue);
|
||||||
|
furi_event_loop_subscribe_message_queue(
|
||||||
|
data->consumer.event_loop,
|
||||||
|
data->message_queue,
|
||||||
|
FuriEventLoopEventIn,
|
||||||
|
test_furi_event_loop_consumer_message_queue_callback,
|
||||||
|
data);
|
||||||
|
|
||||||
|
} else if(data->consumer.message_queue_count == MESSAGE_COUNT) {
|
||||||
|
furi_event_loop_unsubscribe(data->consumer.event_loop, data->message_queue);
|
||||||
|
furi_event_loop_pend_callback(
|
||||||
|
data->consumer.event_loop, test_furi_event_loop_pending_callback, &data->consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_furi_event_loop_consumer_stream_buffer_callback(
|
||||||
|
FuriEventLoopObject* object,
|
||||||
|
void* context) {
|
||||||
|
furi_check(context);
|
||||||
|
|
||||||
|
TestFuriEventLoopData* data = context;
|
||||||
|
furi_check(data->stream_buffer == object);
|
||||||
|
|
||||||
|
TestFuriEventLoopThread* producer = &data->producer;
|
||||||
|
TestFuriEventLoopThread* consumer = &data->consumer;
|
||||||
|
|
||||||
|
furi_delay_us(furi_hal_random_get() % 100);
|
||||||
|
|
||||||
|
furi_check(
|
||||||
|
furi_stream_buffer_receive(
|
||||||
|
data->stream_buffer, &consumer->stream_buffer_count, sizeof(uint32_t), 0) ==
|
||||||
|
sizeof(uint32_t));
|
||||||
|
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG,
|
||||||
|
"consumer StreamBuffer: %lu %lu",
|
||||||
|
producer->stream_buffer_count,
|
||||||
|
consumer->stream_buffer_count);
|
||||||
|
|
||||||
|
if(consumer->stream_buffer_count == MESSAGE_COUNT / 2) {
|
||||||
|
furi_event_loop_unsubscribe(consumer->event_loop, data->stream_buffer);
|
||||||
|
furi_event_loop_subscribe_stream_buffer(
|
||||||
|
consumer->event_loop,
|
||||||
|
data->stream_buffer,
|
||||||
|
FuriEventLoopEventIn,
|
||||||
|
test_furi_event_loop_consumer_stream_buffer_callback,
|
||||||
|
data);
|
||||||
|
|
||||||
|
} else if(consumer->stream_buffer_count == MESSAGE_COUNT) {
|
||||||
|
furi_event_loop_unsubscribe(data->consumer.event_loop, data->stream_buffer);
|
||||||
|
furi_event_loop_pend_callback(
|
||||||
|
consumer->event_loop, test_furi_event_loop_pending_callback, consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_furi_event_loop_consumer_event_flag_callback(FuriEventLoopObject* object, void* context) {
|
||||||
|
furi_check(context);
|
||||||
|
|
||||||
|
TestFuriEventLoopData* data = context;
|
||||||
|
furi_check(data->event_flag == object);
|
||||||
|
|
||||||
|
furi_delay_us(furi_hal_random_get() % 100);
|
||||||
|
|
||||||
|
const uint32_t producer_flags = (1UL << data->producer.event_flag_count);
|
||||||
|
const uint32_t consumer_flags = (1UL << data->consumer.event_flag_count);
|
||||||
|
|
||||||
|
furi_check(
|
||||||
|
furi_event_flag_wait(data->event_flag, consumer_flags, FuriFlagWaitAny, 0) &
|
||||||
|
consumer_flags);
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "consumer EventFlag: 0x%06lX 0x%06lX", producer_flags, consumer_flags);
|
||||||
|
|
||||||
|
if(data->consumer.event_flag_count == EVENT_FLAG_COUNT / 2) {
|
||||||
|
furi_event_loop_unsubscribe(data->consumer.event_loop, data->event_flag);
|
||||||
|
furi_event_loop_subscribe_event_flag(
|
||||||
|
data->consumer.event_loop,
|
||||||
|
data->event_flag,
|
||||||
|
FuriEventLoopEventIn,
|
||||||
|
test_furi_event_loop_consumer_event_flag_callback,
|
||||||
|
data);
|
||||||
|
|
||||||
|
} else if(data->consumer.event_flag_count == EVENT_FLAG_COUNT) {
|
||||||
|
furi_event_loop_unsubscribe(data->consumer.event_loop, data->event_flag);
|
||||||
|
furi_event_loop_pend_callback(
|
||||||
|
data->consumer.event_loop, test_furi_event_loop_pending_callback, &data->consumer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->consumer.event_flag_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_furi_event_loop_consumer_semaphore_callback(FuriEventLoopObject* object, void* context) {
|
||||||
|
furi_check(context);
|
||||||
|
|
||||||
|
TestFuriEventLoopData* data = context;
|
||||||
|
furi_check(data->semaphore == object);
|
||||||
|
|
||||||
|
furi_delay_us(furi_hal_random_get() % 100);
|
||||||
|
|
||||||
|
TestFuriEventLoopThread* producer = &data->producer;
|
||||||
|
TestFuriEventLoopThread* consumer = &data->consumer;
|
||||||
|
|
||||||
|
furi_check(furi_semaphore_acquire(data->semaphore, 0) == FuriStatusOk);
|
||||||
|
|
||||||
|
FURI_LOG_I(
|
||||||
|
TAG, "consumer Semaphore: %lu %lu", producer->semaphore_count, consumer->semaphore_count);
|
||||||
|
|
||||||
|
if(consumer->semaphore_count == MESSAGE_COUNT / 2) {
|
||||||
|
furi_event_loop_unsubscribe(consumer->event_loop, data->semaphore);
|
||||||
|
furi_event_loop_subscribe_semaphore(
|
||||||
|
consumer->event_loop,
|
||||||
|
data->semaphore,
|
||||||
|
FuriEventLoopEventIn,
|
||||||
|
test_furi_event_loop_consumer_semaphore_callback,
|
||||||
|
data);
|
||||||
|
|
||||||
|
} else if(consumer->semaphore_count == MESSAGE_COUNT) {
|
||||||
|
furi_event_loop_unsubscribe(consumer->event_loop, data->semaphore);
|
||||||
|
furi_event_loop_pend_callback(
|
||||||
|
consumer->event_loop, test_furi_event_loop_pending_callback, consumer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->consumer.semaphore_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t test_furi_event_loop_consumer(void* p) {
|
||||||
|
furi_check(p);
|
||||||
|
|
||||||
|
TestFuriEventLoopData* data = p;
|
||||||
|
TestFuriEventLoopThread* consumer = &data->consumer;
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < RUN_COUNT; ++i) {
|
||||||
|
FURI_LOG_I(TAG, "consumer start run %lu", i);
|
||||||
|
|
||||||
|
test_furi_event_loop_thread_init(consumer);
|
||||||
|
|
||||||
|
furi_event_loop_subscribe_message_queue(
|
||||||
|
consumer->event_loop,
|
||||||
|
data->message_queue,
|
||||||
|
FuriEventLoopEventIn,
|
||||||
|
test_furi_event_loop_consumer_message_queue_callback,
|
||||||
|
data);
|
||||||
|
furi_event_loop_subscribe_stream_buffer(
|
||||||
|
consumer->event_loop,
|
||||||
|
data->stream_buffer,
|
||||||
|
FuriEventLoopEventIn,
|
||||||
|
test_furi_event_loop_consumer_stream_buffer_callback,
|
||||||
|
data);
|
||||||
|
furi_event_loop_subscribe_event_flag(
|
||||||
|
consumer->event_loop,
|
||||||
|
data->event_flag,
|
||||||
|
FuriEventLoopEventIn,
|
||||||
|
test_furi_event_loop_consumer_event_flag_callback,
|
||||||
|
data);
|
||||||
|
furi_event_loop_subscribe_semaphore(
|
||||||
|
consumer->event_loop,
|
||||||
|
data->semaphore,
|
||||||
|
FuriEventLoopEventIn,
|
||||||
|
test_furi_event_loop_consumer_semaphore_callback,
|
||||||
|
data);
|
||||||
|
|
||||||
|
test_furi_event_loop_thread_run_and_cleanup(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_I(TAG, "consumer end");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_furi_event_loop(void) {
|
||||||
|
TestFuriEventLoopData data = {};
|
||||||
|
|
||||||
|
data.message_queue = furi_message_queue_alloc(16, sizeof(uint32_t));
|
||||||
|
data.stream_buffer = furi_stream_buffer_alloc(16, sizeof(uint32_t));
|
||||||
|
data.event_flag = furi_event_flag_alloc();
|
||||||
|
data.semaphore = furi_semaphore_alloc(8, 0);
|
||||||
|
|
||||||
|
FuriThread* producer_thread =
|
||||||
|
furi_thread_alloc_ex("producer_thread", 1 * 1024, test_furi_event_loop_producer, &data);
|
||||||
|
furi_thread_start(producer_thread);
|
||||||
|
|
||||||
|
FuriThread* consumer_thread =
|
||||||
|
furi_thread_alloc_ex("consumer_thread", 1 * 1024, test_furi_event_loop_consumer, &data);
|
||||||
|
furi_thread_start(consumer_thread);
|
||||||
|
|
||||||
|
// Wait for thread to complete their tasks
|
||||||
|
furi_thread_join(producer_thread);
|
||||||
|
furi_thread_join(consumer_thread);
|
||||||
|
|
||||||
|
TestFuriEventLoopThread* producer = &data.producer;
|
||||||
|
TestFuriEventLoopThread* consumer = &data.consumer;
|
||||||
|
|
||||||
|
// The test itself
|
||||||
|
mu_assert_int_eq(producer->message_queue_count, consumer->message_queue_count);
|
||||||
|
mu_assert_int_eq(producer->message_queue_count, MESSAGE_COUNT);
|
||||||
|
mu_assert_int_eq(producer->stream_buffer_count, consumer->stream_buffer_count);
|
||||||
|
mu_assert_int_eq(producer->stream_buffer_count, MESSAGE_COUNT);
|
||||||
|
mu_assert_int_eq(producer->event_flag_count, consumer->event_flag_count);
|
||||||
|
mu_assert_int_eq(producer->event_flag_count, EVENT_FLAG_COUNT);
|
||||||
|
mu_assert_int_eq(producer->semaphore_count, consumer->semaphore_count);
|
||||||
|
mu_assert_int_eq(producer->semaphore_count, MESSAGE_COUNT);
|
||||||
|
|
||||||
|
// Release memory
|
||||||
|
furi_thread_free(consumer_thread);
|
||||||
|
furi_thread_free(producer_thread);
|
||||||
|
|
||||||
|
furi_message_queue_free(data.message_queue);
|
||||||
|
furi_stream_buffer_free(data.stream_buffer);
|
||||||
|
furi_event_flag_free(data.event_flag);
|
||||||
|
furi_semaphore_free(data.semaphore);
|
||||||
|
}
|
||||||
103
applications/debug/unit_tests/tests/furi/furi_primitives_test.c
Normal file
103
applications/debug/unit_tests/tests/furi/furi_primitives_test.c
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
#include "../test.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
#define MESSAGE_QUEUE_CAPACITY (16U)
|
||||||
|
#define MESSAGE_QUEUE_ELEMENT_SIZE (sizeof(uint32_t))
|
||||||
|
|
||||||
|
#define STREAM_BUFFER_SIZE (32U)
|
||||||
|
#define STREAM_BUFFER_TRG_LEVEL (STREAM_BUFFER_SIZE / 2U)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FuriMessageQueue* message_queue;
|
||||||
|
FuriStreamBuffer* stream_buffer;
|
||||||
|
} TestFuriPrimitivesData;
|
||||||
|
|
||||||
|
static void test_furi_message_queue(TestFuriPrimitivesData* data) {
|
||||||
|
FuriMessageQueue* message_queue = data->message_queue;
|
||||||
|
|
||||||
|
mu_assert_int_eq(0, furi_message_queue_get_count(message_queue));
|
||||||
|
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_space(message_queue));
|
||||||
|
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_capacity(message_queue));
|
||||||
|
mu_assert_int_eq(
|
||||||
|
MESSAGE_QUEUE_ELEMENT_SIZE, furi_message_queue_get_message_size(message_queue));
|
||||||
|
|
||||||
|
for(uint32_t i = 0;; ++i) {
|
||||||
|
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY - i, furi_message_queue_get_space(message_queue));
|
||||||
|
mu_assert_int_eq(i, furi_message_queue_get_count(message_queue));
|
||||||
|
|
||||||
|
if(furi_message_queue_put(message_queue, &i, 0) != FuriStatusOk) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mu_assert_int_eq(0, furi_message_queue_get_space(message_queue));
|
||||||
|
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_count(message_queue));
|
||||||
|
|
||||||
|
for(uint32_t i = 0;; ++i) {
|
||||||
|
mu_assert_int_eq(i, furi_message_queue_get_space(message_queue));
|
||||||
|
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY - i, furi_message_queue_get_count(message_queue));
|
||||||
|
|
||||||
|
uint32_t value;
|
||||||
|
if(furi_message_queue_get(message_queue, &value, 0) != FuriStatusOk) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mu_assert_int_eq(i, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
mu_assert_int_eq(0, furi_message_queue_get_count(message_queue));
|
||||||
|
mu_assert_int_eq(MESSAGE_QUEUE_CAPACITY, furi_message_queue_get_space(message_queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_furi_stream_buffer(TestFuriPrimitivesData* data) {
|
||||||
|
FuriStreamBuffer* stream_buffer = data->stream_buffer;
|
||||||
|
|
||||||
|
mu_assert(furi_stream_buffer_is_empty(stream_buffer), "Must be empty");
|
||||||
|
mu_assert(!furi_stream_buffer_is_full(stream_buffer), "Must be not full");
|
||||||
|
mu_assert_int_eq(0, furi_stream_buffer_bytes_available(stream_buffer));
|
||||||
|
mu_assert_int_eq(STREAM_BUFFER_SIZE, furi_stream_buffer_spaces_available(stream_buffer));
|
||||||
|
|
||||||
|
for(uint8_t i = 0;; ++i) {
|
||||||
|
mu_assert_int_eq(i, furi_stream_buffer_bytes_available(stream_buffer));
|
||||||
|
mu_assert_int_eq(
|
||||||
|
STREAM_BUFFER_SIZE - i, furi_stream_buffer_spaces_available(stream_buffer));
|
||||||
|
|
||||||
|
if(furi_stream_buffer_send(stream_buffer, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mu_assert(!furi_stream_buffer_is_empty(stream_buffer), "Must be not empty");
|
||||||
|
mu_assert(furi_stream_buffer_is_full(stream_buffer), "Must be full");
|
||||||
|
mu_assert_int_eq(STREAM_BUFFER_SIZE, furi_stream_buffer_bytes_available(stream_buffer));
|
||||||
|
mu_assert_int_eq(0, furi_stream_buffer_spaces_available(stream_buffer));
|
||||||
|
|
||||||
|
for(uint8_t i = 0;; ++i) {
|
||||||
|
mu_assert_int_eq(
|
||||||
|
STREAM_BUFFER_SIZE - i, furi_stream_buffer_bytes_available(stream_buffer));
|
||||||
|
mu_assert_int_eq(i, furi_stream_buffer_spaces_available(stream_buffer));
|
||||||
|
|
||||||
|
uint8_t value;
|
||||||
|
if(furi_stream_buffer_receive(stream_buffer, &value, sizeof(uint8_t), 0) !=
|
||||||
|
sizeof(uint8_t)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mu_assert_int_eq(i, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a stub that needs expanding
|
||||||
|
void test_furi_primitives(void) {
|
||||||
|
TestFuriPrimitivesData data = {
|
||||||
|
.message_queue =
|
||||||
|
furi_message_queue_alloc(MESSAGE_QUEUE_CAPACITY, MESSAGE_QUEUE_ELEMENT_SIZE),
|
||||||
|
.stream_buffer = furi_stream_buffer_alloc(STREAM_BUFFER_SIZE, STREAM_BUFFER_TRG_LEVEL),
|
||||||
|
};
|
||||||
|
|
||||||
|
test_furi_message_queue(&data);
|
||||||
|
test_furi_stream_buffer(&data);
|
||||||
|
|
||||||
|
furi_message_queue_free(data.message_queue);
|
||||||
|
furi_stream_buffer_free(data.stream_buffer);
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ void test_furi_pubsub(void);
|
|||||||
void test_furi_memmgr(void);
|
void test_furi_memmgr(void);
|
||||||
void test_furi_event_loop(void);
|
void test_furi_event_loop(void);
|
||||||
void test_errno_saving(void);
|
void test_errno_saving(void);
|
||||||
|
void test_furi_primitives(void);
|
||||||
|
|
||||||
static int foo = 0;
|
static int foo = 0;
|
||||||
|
|
||||||
@@ -47,6 +48,10 @@ MU_TEST(mu_test_errno_saving) {
|
|||||||
test_errno_saving();
|
test_errno_saving();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MU_TEST(mu_test_furi_primitives) {
|
||||||
|
test_furi_primitives();
|
||||||
|
}
|
||||||
|
|
||||||
MU_TEST_SUITE(test_suite) {
|
MU_TEST_SUITE(test_suite) {
|
||||||
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
|
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
|
||||||
MU_RUN_TEST(test_check);
|
MU_RUN_TEST(test_check);
|
||||||
@@ -57,6 +62,7 @@ MU_TEST_SUITE(test_suite) {
|
|||||||
MU_RUN_TEST(mu_test_furi_memmgr);
|
MU_RUN_TEST(mu_test_furi_memmgr);
|
||||||
MU_RUN_TEST(mu_test_furi_event_loop);
|
MU_RUN_TEST(mu_test_furi_event_loop);
|
||||||
MU_RUN_TEST(mu_test_errno_saving);
|
MU_RUN_TEST(mu_test_errno_saving);
|
||||||
|
MU_RUN_TEST(mu_test_furi_primitives);
|
||||||
}
|
}
|
||||||
|
|
||||||
int run_minunit_test_furi(void) {
|
int run_minunit_test_furi(void) {
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
App(
|
||||||
|
appid="example_event_loop_event_flags",
|
||||||
|
name="Example: Event Loop Event Flags",
|
||||||
|
apptype=FlipperAppType.EXTERNAL,
|
||||||
|
sources=["example_event_loop_event_flags.c"],
|
||||||
|
entry_point="example_event_loop_event_flags_app",
|
||||||
|
fap_category="Examples",
|
||||||
|
)
|
||||||
|
|
||||||
App(
|
App(
|
||||||
appid="example_event_loop_timer",
|
appid="example_event_loop_timer",
|
||||||
name="Example: Event Loop Timer",
|
name="Example: Event Loop Timer",
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/**
|
||||||
|
* @file example_event_loop_event_flags.c
|
||||||
|
* @brief Example application demonstrating the use of the FuriEventFlag primitive in FuriEventLoop instances.
|
||||||
|
*
|
||||||
|
* This application receives keystrokes from the input service and sets the appropriate flags,
|
||||||
|
* which are subsequently processed in the event loop
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/view_port.h>
|
||||||
|
|
||||||
|
#include <furi_hal_random.h>
|
||||||
|
|
||||||
|
#define TAG "ExampleEventLoopEventFlags"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Gui* gui;
|
||||||
|
ViewPort* view_port;
|
||||||
|
FuriEventLoop* event_loop;
|
||||||
|
FuriEventFlag* event_flag;
|
||||||
|
} EventLoopEventFlagsApp;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
EventLoopEventFlagsOk = (1 << 0),
|
||||||
|
EventLoopEventFlagsUp = (1 << 1),
|
||||||
|
EventLoopEventFlagsDown = (1 << 2),
|
||||||
|
EventLoopEventFlagsLeft = (1 << 3),
|
||||||
|
EventLoopEventFlagsRight = (1 << 4),
|
||||||
|
EventLoopEventFlagsBack = (1 << 5),
|
||||||
|
EventLoopEventFlagsExit = (1 << 6),
|
||||||
|
} EventLoopEventFlags;
|
||||||
|
|
||||||
|
#define EVENT_LOOP_EVENT_FLAGS_MASK \
|
||||||
|
(EventLoopEventFlagsOk | EventLoopEventFlagsUp | EventLoopEventFlagsDown | \
|
||||||
|
EventLoopEventFlagsLeft | EventLoopEventFlagsRight | EventLoopEventFlagsBack | \
|
||||||
|
EventLoopEventFlagsExit)
|
||||||
|
|
||||||
|
// This function is executed in the GUI context each time an input event occurs (e.g. the user pressed a key)
|
||||||
|
static void event_loop_event_flags_app_input_callback(InputEvent* event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
EventLoopEventFlagsApp* app = context;
|
||||||
|
UNUSED(app);
|
||||||
|
|
||||||
|
if(event->type == InputTypePress) {
|
||||||
|
if(event->key == InputKeyOk) {
|
||||||
|
furi_event_flag_set(app->event_flag, EventLoopEventFlagsOk);
|
||||||
|
} else if(event->key == InputKeyUp) {
|
||||||
|
furi_event_flag_set(app->event_flag, EventLoopEventFlagsUp);
|
||||||
|
} else if(event->key == InputKeyDown) {
|
||||||
|
furi_event_flag_set(app->event_flag, EventLoopEventFlagsDown);
|
||||||
|
} else if(event->key == InputKeyLeft) {
|
||||||
|
furi_event_flag_set(app->event_flag, EventLoopEventFlagsLeft);
|
||||||
|
} else if(event->key == InputKeyRight) {
|
||||||
|
furi_event_flag_set(app->event_flag, EventLoopEventFlagsRight);
|
||||||
|
} else if(event->key == InputKeyBack) {
|
||||||
|
furi_event_flag_set(app->event_flag, EventLoopEventFlagsBack);
|
||||||
|
}
|
||||||
|
} else if(event->type == InputTypeLong) {
|
||||||
|
if(event->key == InputKeyBack) {
|
||||||
|
furi_event_flag_set(app->event_flag, EventLoopEventFlagsExit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is executed each time a new event flag is inserted in the input event flag.
|
||||||
|
static void
|
||||||
|
event_loop_event_flags_app_event_flags_callback(FuriEventLoopObject* object, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
EventLoopEventFlagsApp* app = context;
|
||||||
|
|
||||||
|
furi_assert(object == app->event_flag);
|
||||||
|
|
||||||
|
EventLoopEventFlags events =
|
||||||
|
furi_event_flag_wait(app->event_flag, EVENT_LOOP_EVENT_FLAGS_MASK, FuriFlagWaitAny, 0);
|
||||||
|
furi_check((events) != 0);
|
||||||
|
|
||||||
|
if(events & EventLoopEventFlagsOk) {
|
||||||
|
FURI_LOG_I(TAG, "Press \"Ok\"");
|
||||||
|
}
|
||||||
|
if(events & EventLoopEventFlagsUp) {
|
||||||
|
FURI_LOG_I(TAG, "Press \"Up\"");
|
||||||
|
}
|
||||||
|
if(events & EventLoopEventFlagsDown) {
|
||||||
|
FURI_LOG_I(TAG, "Press \"Down\"");
|
||||||
|
}
|
||||||
|
if(events & EventLoopEventFlagsLeft) {
|
||||||
|
FURI_LOG_I(TAG, "Press \"Left\"");
|
||||||
|
}
|
||||||
|
if(events & EventLoopEventFlagsRight) {
|
||||||
|
FURI_LOG_I(TAG, "Press \"Right\"");
|
||||||
|
}
|
||||||
|
if(events & EventLoopEventFlagsBack) {
|
||||||
|
FURI_LOG_I(TAG, "Press \"Back\"");
|
||||||
|
}
|
||||||
|
if(events & EventLoopEventFlagsExit) {
|
||||||
|
FURI_LOG_I(TAG, "Exit App");
|
||||||
|
furi_event_loop_stop(app->event_loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static EventLoopEventFlagsApp* event_loop_event_flags_app_alloc(void) {
|
||||||
|
EventLoopEventFlagsApp* app = malloc(sizeof(EventLoopEventFlagsApp));
|
||||||
|
|
||||||
|
// Create event loop instances.
|
||||||
|
app->event_loop = furi_event_loop_alloc();
|
||||||
|
// Create event flag instances.
|
||||||
|
app->event_flag = furi_event_flag_alloc();
|
||||||
|
|
||||||
|
// Create GUI instance.
|
||||||
|
app->gui = furi_record_open(RECORD_GUI);
|
||||||
|
app->view_port = view_port_alloc();
|
||||||
|
// Gain exclusive access to the input events
|
||||||
|
view_port_input_callback_set(app->view_port, event_loop_event_flags_app_input_callback, app);
|
||||||
|
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
|
||||||
|
|
||||||
|
// Notify the event loop about incoming messages in the event flag
|
||||||
|
furi_event_loop_subscribe_event_flag(
|
||||||
|
app->event_loop,
|
||||||
|
app->event_flag,
|
||||||
|
FuriEventLoopEventIn | FuriEventLoopEventFlagEdge,
|
||||||
|
event_loop_event_flags_app_event_flags_callback,
|
||||||
|
app);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_loop_event_flags_app_free(EventLoopEventFlagsApp* app) {
|
||||||
|
gui_remove_view_port(app->gui, app->view_port);
|
||||||
|
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
app->gui = NULL;
|
||||||
|
|
||||||
|
// Delete all instances
|
||||||
|
view_port_free(app->view_port);
|
||||||
|
app->view_port = NULL;
|
||||||
|
|
||||||
|
// IMPORTANT: The user code MUST unsubscribe from all events before deleting the event loop.
|
||||||
|
// Failure to do so will result in a crash.
|
||||||
|
furi_event_loop_unsubscribe(app->event_loop, app->event_flag);
|
||||||
|
|
||||||
|
furi_event_flag_free(app->event_flag);
|
||||||
|
app->event_flag = NULL;
|
||||||
|
|
||||||
|
furi_event_loop_free(app->event_loop);
|
||||||
|
app->event_loop = NULL;
|
||||||
|
|
||||||
|
free(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_loop_event_flags_app_run(EventLoopEventFlagsApp* app) {
|
||||||
|
FURI_LOG_I(TAG, "Press keys to see them printed here.");
|
||||||
|
FURI_LOG_I(TAG, "Quickly press different keys to generate events.");
|
||||||
|
FURI_LOG_I(TAG, "Long press \"Back\" to exit app.");
|
||||||
|
|
||||||
|
// Run the application event loop. This call will block until the application is about to exit.
|
||||||
|
furi_event_loop_run(app->event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
* vvv START HERE vvv
|
||||||
|
*
|
||||||
|
* The application's entry point - referenced in application.fam
|
||||||
|
*******************************************************************/
|
||||||
|
int32_t example_event_loop_event_flags_app(void* arg) {
|
||||||
|
UNUSED(arg);
|
||||||
|
|
||||||
|
EventLoopEventFlagsApp* app = event_loop_event_flags_app_alloc();
|
||||||
|
event_loop_event_flags_app_run(app);
|
||||||
|
event_loop_event_flags_app_free(app);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// This function is executed each time the data is taken out of the stream buffer. It is used to restart the worker timer.
|
// This function is executed each time the data is taken out of the stream buffer. It is used to restart the worker timer.
|
||||||
static bool
|
static void
|
||||||
event_loop_multi_app_stream_buffer_worker_callback(FuriEventLoopObject* object, void* context) {
|
event_loop_multi_app_stream_buffer_worker_callback(FuriEventLoopObject* object, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
EventLoopMultiAppWorker* worker = context;
|
EventLoopMultiAppWorker* worker = context;
|
||||||
@@ -62,8 +62,6 @@ static bool
|
|||||||
FURI_LOG_I(TAG, "Data was removed from buffer");
|
FURI_LOG_I(TAG, "Data was removed from buffer");
|
||||||
// Restart the timer to generate another block of random data.
|
// Restart the timer to generate another block of random data.
|
||||||
furi_event_loop_timer_start(worker->timer, WORKER_DATA_INTERVAL_MS);
|
furi_event_loop_timer_start(worker->timer, WORKER_DATA_INTERVAL_MS);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is executed when the worker timer expires. The timer will NOT restart automatically
|
// This function is executed when the worker timer expires. The timer will NOT restart automatically
|
||||||
@@ -152,7 +150,7 @@ static void event_loop_multi_app_input_callback(InputEvent* event, void* context
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This function is executed each time new data is available in the stream buffer.
|
// This function is executed each time new data is available in the stream buffer.
|
||||||
static bool
|
static void
|
||||||
event_loop_multi_app_stream_buffer_callback(FuriEventLoopObject* object, void* context) {
|
event_loop_multi_app_stream_buffer_callback(FuriEventLoopObject* object, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
EventLoopMultiApp* app = context;
|
EventLoopMultiApp* app = context;
|
||||||
@@ -172,12 +170,10 @@ static bool
|
|||||||
|
|
||||||
FURI_LOG_I(TAG, "Received data: %s", furi_string_get_cstr(tmp_str));
|
FURI_LOG_I(TAG, "Received data: %s", furi_string_get_cstr(tmp_str));
|
||||||
furi_string_free(tmp_str);
|
furi_string_free(tmp_str);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is executed each time a new message is inserted in the input queue.
|
// This function is executed each time a new message is inserted in the input queue.
|
||||||
static bool event_loop_multi_app_input_queue_callback(FuriEventLoopObject* object, void* context) {
|
static void event_loop_multi_app_input_queue_callback(FuriEventLoopObject* object, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
EventLoopMultiApp* app = context;
|
EventLoopMultiApp* app = context;
|
||||||
|
|
||||||
@@ -222,8 +218,6 @@ static bool event_loop_multi_app_input_queue_callback(FuriEventLoopObject* objec
|
|||||||
// Not a long press, just print the key's name.
|
// Not a long press, just print the key's name.
|
||||||
FURI_LOG_I(TAG, "Short press: %s", input_get_key_name(event.key));
|
FURI_LOG_I(TAG, "Short press: %s", input_get_key_name(event.key));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is executed each time the countdown timer expires.
|
// This function is executed each time the countdown timer expires.
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ static int32_t event_loop_mutex_app_worker_thread(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This function is being run each time when the mutex gets released
|
// This function is being run each time when the mutex gets released
|
||||||
static bool event_loop_mutex_app_event_callback(FuriEventLoopObject* object, void* context) {
|
static void event_loop_mutex_app_event_callback(FuriEventLoopObject* object, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
|
|
||||||
EventLoopMutexApp* app = context;
|
EventLoopMutexApp* app = context;
|
||||||
@@ -82,8 +82,6 @@ static bool event_loop_mutex_app_event_callback(FuriEventLoopObject* object, voi
|
|||||||
MUTEX_EVENT_AND_FLAGS,
|
MUTEX_EVENT_AND_FLAGS,
|
||||||
event_loop_mutex_app_event_callback,
|
event_loop_mutex_app_event_callback,
|
||||||
app);
|
app);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static EventLoopMutexApp* event_loop_mutex_app_alloc(void) {
|
static EventLoopMutexApp* event_loop_mutex_app_alloc(void) {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ static int32_t event_loop_stream_buffer_app_worker_thread(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This function is being run each time when the number of bytes in the buffer is above its trigger level.
|
// This function is being run each time when the number of bytes in the buffer is above its trigger level.
|
||||||
static bool
|
static void
|
||||||
event_loop_stream_buffer_app_event_callback(FuriEventLoopObject* object, void* context) {
|
event_loop_stream_buffer_app_event_callback(FuriEventLoopObject* object, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
EventLoopStreamBufferApp* app = context;
|
EventLoopStreamBufferApp* app = context;
|
||||||
@@ -76,8 +76,6 @@ static bool
|
|||||||
|
|
||||||
FURI_LOG_I(TAG, "Received data: %s", furi_string_get_cstr(tmp_str));
|
FURI_LOG_I(TAG, "Received data: %s", furi_string_get_cstr(tmp_str));
|
||||||
furi_string_free(tmp_str);
|
furi_string_free(tmp_str);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static EventLoopStreamBufferApp* event_loop_stream_buffer_app_alloc(void) {
|
static EventLoopStreamBufferApp* event_loop_stream_buffer_app_alloc(void) {
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ static void dolphin_update_clear_limits_timer_period(void* context) {
|
|||||||
FURI_LOG_D(TAG, "Daily limits reset in %lu ms", time_to_clear_limits);
|
FURI_LOG_D(TAG, "Daily limits reset in %lu ms", time_to_clear_limits);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool dolphin_process_event(FuriEventLoopObject* object, void* context) {
|
static void dolphin_process_event(FuriEventLoopObject* object, void* context) {
|
||||||
UNUSED(object);
|
UNUSED(object);
|
||||||
|
|
||||||
Dolphin* dolphin = context;
|
Dolphin* dolphin = context;
|
||||||
@@ -264,8 +264,6 @@ static bool dolphin_process_event(FuriEventLoopObject* object, void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dolphin_event_release(&event);
|
dolphin_event_release(&event);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dolphin_storage_callback(const void* message, void* context) {
|
static void dolphin_storage_callback(const void* message, void* context) {
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ void view_dispatcher_update(View* view, void* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context) {
|
void view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
ViewDispatcher* instance = context;
|
ViewDispatcher* instance = context;
|
||||||
furi_assert(instance->event_queue == object);
|
furi_assert(instance->event_queue == object);
|
||||||
@@ -384,11 +384,9 @@ bool view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* conte
|
|||||||
uint32_t event;
|
uint32_t event;
|
||||||
furi_check(furi_message_queue_get(instance->event_queue, &event, 0) == FuriStatusOk);
|
furi_check(furi_message_queue_get(instance->event_queue, &event, 0) == FuriStatusOk);
|
||||||
view_dispatcher_handle_custom_event(instance, event);
|
view_dispatcher_handle_custom_event(instance, event);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context) {
|
void view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
ViewDispatcher* instance = context;
|
ViewDispatcher* instance = context;
|
||||||
furi_assert(instance->input_queue == object);
|
furi_assert(instance->input_queue == object);
|
||||||
@@ -396,6 +394,4 @@ bool view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* conte
|
|||||||
InputEvent input;
|
InputEvent input;
|
||||||
furi_check(furi_message_queue_get(instance->input_queue, &input, 0) == FuriStatusOk);
|
furi_check(furi_message_queue_get(instance->input_queue, &input, 0) == FuriStatusOk);
|
||||||
view_dispatcher_handle_input(instance, &input);
|
view_dispatcher_handle_input(instance, &input);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie
|
|||||||
void view_dispatcher_update(View* view, void* context);
|
void view_dispatcher_update(View* view, void* context);
|
||||||
|
|
||||||
/** ViewDispatcher run event loop event callback */
|
/** ViewDispatcher run event loop event callback */
|
||||||
bool view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context);
|
void view_dispatcher_run_event_callback(FuriEventLoopObject* object, void* context);
|
||||||
|
|
||||||
/** ViewDispatcher run event loop input callback */
|
/** ViewDispatcher run event loop input callback */
|
||||||
bool view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context);
|
void view_dispatcher_run_input_callback(FuriEventLoopObject* object, void* context);
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ static void power_handle_reboot(PowerBootMode mode) {
|
|||||||
furi_hal_power_reset();
|
furi_hal_power_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool power_message_callback(FuriEventLoopObject* object, void* context) {
|
static void power_message_callback(FuriEventLoopObject* object, void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
Power* power = context;
|
Power* power = context;
|
||||||
|
|
||||||
@@ -223,8 +223,6 @@ static bool power_message_callback(FuriEventLoopObject* object, void* context) {
|
|||||||
if(msg.lock) {
|
if(msg.lock) {
|
||||||
api_lock_unlock(msg.lock);
|
api_lock_unlock(msg.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void power_tick_callback(void* context) {
|
static void power_tick_callback(void* context) {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ static void js_event_loop_callback_generic(void* param) {
|
|||||||
/**
|
/**
|
||||||
* @brief Handles non-timer events
|
* @brief Handles non-timer events
|
||||||
*/
|
*/
|
||||||
static bool js_event_loop_callback(void* object, void* param) {
|
static void js_event_loop_callback(void* object, void* param) {
|
||||||
JsEventLoopCallbackContext* context = param;
|
JsEventLoopCallbackContext* context = param;
|
||||||
|
|
||||||
if(context->transformer) {
|
if(context->transformer) {
|
||||||
@@ -102,8 +102,6 @@ static bool js_event_loop_callback(void* object, void* param) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
js_event_loop_callback_generic(param);
|
js_event_loop_callback_generic(param);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,11 +5,15 @@
|
|||||||
#include <FreeRTOS.h>
|
#include <FreeRTOS.h>
|
||||||
#include <event_groups.h>
|
#include <event_groups.h>
|
||||||
|
|
||||||
|
#include "event_loop_link_i.h"
|
||||||
|
|
||||||
#define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U
|
#define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U
|
||||||
#define FURI_EVENT_FLAG_INVALID_BITS (~((1UL << FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U))
|
#define FURI_EVENT_FLAG_VALID_BITS ((1UL << FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U)
|
||||||
|
#define FURI_EVENT_FLAG_INVALID_BITS (~(FURI_EVENT_FLAG_VALID_BITS))
|
||||||
|
|
||||||
struct FuriEventFlag {
|
struct FuriEventFlag {
|
||||||
StaticEventGroup_t container;
|
StaticEventGroup_t container;
|
||||||
|
FuriEventLoopLink event_loop_link;
|
||||||
};
|
};
|
||||||
|
|
||||||
// IMPORTANT: container MUST be the FIRST struct member
|
// IMPORTANT: container MUST be the FIRST struct member
|
||||||
@@ -27,6 +31,11 @@ FuriEventFlag* furi_event_flag_alloc(void) {
|
|||||||
|
|
||||||
void furi_event_flag_free(FuriEventFlag* instance) {
|
void furi_event_flag_free(FuriEventFlag* instance) {
|
||||||
furi_check(!FURI_IS_IRQ_MODE());
|
furi_check(!FURI_IS_IRQ_MODE());
|
||||||
|
|
||||||
|
// Event Loop must be disconnected
|
||||||
|
furi_check(!instance->event_loop_link.item_in);
|
||||||
|
furi_check(!instance->event_loop_link.item_out);
|
||||||
|
|
||||||
vEventGroupDelete((EventGroupHandle_t)instance);
|
vEventGroupDelete((EventGroupHandle_t)instance);
|
||||||
free(instance);
|
free(instance);
|
||||||
}
|
}
|
||||||
@@ -39,6 +48,8 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) {
|
|||||||
uint32_t rflags;
|
uint32_t rflags;
|
||||||
BaseType_t yield;
|
BaseType_t yield;
|
||||||
|
|
||||||
|
FURI_CRITICAL_ENTER();
|
||||||
|
|
||||||
if(FURI_IS_IRQ_MODE()) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
yield = pdFALSE;
|
yield = pdFALSE;
|
||||||
if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) {
|
if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) {
|
||||||
@@ -48,11 +59,15 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) {
|
|||||||
portYIELD_FROM_ISR(yield);
|
portYIELD_FROM_ISR(yield);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vTaskSuspendAll();
|
|
||||||
rflags = xEventGroupSetBits(hEventGroup, (EventBits_t)flags);
|
rflags = xEventGroupSetBits(hEventGroup, (EventBits_t)flags);
|
||||||
(void)xTaskResumeAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(rflags & flags) {
|
||||||
|
furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_CRITICAL_EXIT();
|
||||||
|
|
||||||
/* Return event flags after setting */
|
/* Return event flags after setting */
|
||||||
return rflags;
|
return rflags;
|
||||||
}
|
}
|
||||||
@@ -64,6 +79,7 @@ uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) {
|
|||||||
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
|
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance;
|
||||||
uint32_t rflags;
|
uint32_t rflags;
|
||||||
|
|
||||||
|
FURI_CRITICAL_ENTER();
|
||||||
if(FURI_IS_IRQ_MODE()) {
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
rflags = xEventGroupGetBitsFromISR(hEventGroup);
|
rflags = xEventGroupGetBitsFromISR(hEventGroup);
|
||||||
|
|
||||||
@@ -79,6 +95,11 @@ uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) {
|
|||||||
rflags = xEventGroupClearBits(hEventGroup, (EventBits_t)flags);
|
rflags = xEventGroupClearBits(hEventGroup, (EventBits_t)flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(rflags & flags) {
|
||||||
|
furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut);
|
||||||
|
}
|
||||||
|
FURI_CRITICAL_EXIT();
|
||||||
|
|
||||||
/* Return event flags before clearing */
|
/* Return event flags before clearing */
|
||||||
return rflags;
|
return rflags;
|
||||||
}
|
}
|
||||||
@@ -146,6 +167,36 @@ uint32_t furi_event_flag_wait(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if((rflags & FuriFlagError) == 0U) {
|
||||||
|
furi_event_loop_link_notify(&instance->event_loop_link, FuriEventLoopEventOut);
|
||||||
|
}
|
||||||
|
|
||||||
/* Return event flags before clearing */
|
/* Return event flags before clearing */
|
||||||
return rflags;
|
return rflags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FuriEventLoopLink* furi_event_flag_event_loop_get_link(FuriEventLoopObject* object) {
|
||||||
|
FuriEventFlag* instance = object;
|
||||||
|
furi_assert(instance);
|
||||||
|
return &instance->event_loop_link;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
furi_event_flag_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {
|
||||||
|
FuriEventFlag* instance = object;
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
if(event == FuriEventLoopEventIn) {
|
||||||
|
return (furi_event_flag_get(instance) & FURI_EVENT_FLAG_VALID_BITS);
|
||||||
|
} else if(event == FuriEventLoopEventOut) {
|
||||||
|
return (furi_event_flag_get(instance) & FURI_EVENT_FLAG_VALID_BITS) !=
|
||||||
|
FURI_EVENT_FLAG_VALID_BITS;
|
||||||
|
} else {
|
||||||
|
furi_crash();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FuriEventLoopContract furi_event_flag_event_loop_contract = {
|
||||||
|
.get_link = furi_event_flag_event_loop_get_link,
|
||||||
|
.get_level = furi_event_flag_event_loop_get_level,
|
||||||
|
};
|
||||||
|
|||||||
@@ -101,36 +101,39 @@ void furi_event_loop_free(FuriEventLoop* instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline FuriEventLoopProcessStatus
|
static inline FuriEventLoopProcessStatus
|
||||||
furi_event_loop_poll_process_level_event(FuriEventLoopItem* item) {
|
furi_event_loop_process_edge_event(FuriEventLoopItem* item) {
|
||||||
if(!item->contract->get_level(item->object, item->event)) {
|
FuriEventLoopProcessStatus status = FuriEventLoopProcessStatusComplete;
|
||||||
return FuriEventLoopProcessStatusComplete;
|
item->callback(item->object, item->callback_context);
|
||||||
} else if(item->callback(item->object, item->callback_context)) {
|
|
||||||
return FuriEventLoopProcessStatusIncomplete;
|
return status;
|
||||||
} else {
|
|
||||||
return FuriEventLoopProcessStatusAgain;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline FuriEventLoopProcessStatus
|
static inline FuriEventLoopProcessStatus
|
||||||
furi_event_loop_poll_process_edge_event(FuriEventLoopItem* item) {
|
furi_event_loop_process_level_event(FuriEventLoopItem* item) {
|
||||||
if(item->callback(item->object, item->callback_context)) {
|
FuriEventLoopProcessStatus status = FuriEventLoopProcessStatusComplete;
|
||||||
return FuriEventLoopProcessStatusComplete;
|
if(item->contract->get_level(item->object, item->event)) {
|
||||||
} else {
|
item->callback(item->object, item->callback_context);
|
||||||
return FuriEventLoopProcessStatusAgain;
|
|
||||||
|
if(item->contract->get_level(item->object, item->event)) {
|
||||||
|
status = FuriEventLoopProcessStatusIncomplete;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline FuriEventLoopProcessStatus
|
static inline FuriEventLoopProcessStatus
|
||||||
furi_event_loop_poll_process_event(FuriEventLoop* instance, FuriEventLoopItem* item) {
|
furi_event_loop_process_event(FuriEventLoop* instance, FuriEventLoopItem* item) {
|
||||||
FuriEventLoopProcessStatus status;
|
FuriEventLoopProcessStatus status;
|
||||||
|
|
||||||
if(item->event & FuriEventLoopEventFlagOnce) {
|
if(item->event & FuriEventLoopEventFlagOnce) {
|
||||||
furi_event_loop_unsubscribe(instance, item->object);
|
furi_event_loop_unsubscribe(instance, item->object);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(item->event & FuriEventLoopEventFlagEdge) {
|
if(item->event & FuriEventLoopEventFlagEdge) {
|
||||||
status = furi_event_loop_poll_process_edge_event(item);
|
status = furi_event_loop_process_edge_event(item);
|
||||||
} else {
|
} else {
|
||||||
status = furi_event_loop_poll_process_level_event(item);
|
status = furi_event_loop_process_level_event(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(item->owner == NULL) {
|
if(item->owner == NULL) {
|
||||||
@@ -140,7 +143,7 @@ static inline FuriEventLoopProcessStatus
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void furi_event_loop_process_waiting_list(FuriEventLoop* instance) {
|
static inline FuriEventLoopItem* furi_event_loop_get_waiting_item(FuriEventLoop* instance) {
|
||||||
FuriEventLoopItem* item = NULL;
|
FuriEventLoopItem* item = NULL;
|
||||||
|
|
||||||
FURI_CRITICAL_ENTER();
|
FURI_CRITICAL_ENTER();
|
||||||
@@ -152,27 +155,42 @@ static void furi_event_loop_process_waiting_list(FuriEventLoop* instance) {
|
|||||||
|
|
||||||
FURI_CRITICAL_EXIT();
|
FURI_CRITICAL_EXIT();
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void furi_event_loop_sync_flags(FuriEventLoop* instance) {
|
||||||
|
FURI_CRITICAL_ENTER();
|
||||||
|
|
||||||
|
if(!WaitingList_empty_p(instance->waiting_list)) {
|
||||||
|
xTaskNotifyIndexed(
|
||||||
|
(TaskHandle_t)instance->thread_id,
|
||||||
|
FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX,
|
||||||
|
FuriEventLoopFlagEvent,
|
||||||
|
eSetBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_CRITICAL_EXIT();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void furi_event_loop_process_waiting_list(FuriEventLoop* instance) {
|
||||||
|
FuriEventLoopItem* item = furi_event_loop_get_waiting_item(instance);
|
||||||
if(!item) return;
|
if(!item) return;
|
||||||
|
|
||||||
while(true) {
|
FuriEventLoopProcessStatus status = furi_event_loop_process_event(instance, item);
|
||||||
FuriEventLoopProcessStatus ret = furi_event_loop_poll_process_event(instance, item);
|
|
||||||
|
|
||||||
if(ret == FuriEventLoopProcessStatusComplete) {
|
if(status == FuriEventLoopProcessStatusComplete) {
|
||||||
// Event processing complete, break from loop
|
// Event processing complete, do nothing
|
||||||
break;
|
} else if(status == FuriEventLoopProcessStatusIncomplete) {
|
||||||
} else if(ret == FuriEventLoopProcessStatusIncomplete) {
|
// Event processing incomplete, put item back in waiting list
|
||||||
// Event processing incomplete more processing needed
|
furi_event_loop_item_notify(item);
|
||||||
} else if(ret == FuriEventLoopProcessStatusAgain) { //-V547
|
} else if(status == FuriEventLoopProcessStatusFreeLater) { //-V547
|
||||||
furi_event_loop_item_notify(item);
|
// Unsubscribed from inside the callback, delete item
|
||||||
break;
|
furi_event_loop_item_free(item);
|
||||||
// Unsubscribed from inside the callback, delete item
|
} else {
|
||||||
} else if(ret == FuriEventLoopProcessStatusFreeLater) { //-V547
|
furi_crash();
|
||||||
furi_event_loop_item_free(item);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
furi_crash();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
furi_event_loop_sync_flags(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void furi_event_loop_restore_flags(FuriEventLoop* instance, uint32_t flags) {
|
static void furi_event_loop_restore_flags(FuriEventLoop* instance, uint32_t flags) {
|
||||||
@@ -239,14 +257,28 @@ void furi_event_loop_run(FuriEventLoop* instance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void furi_event_loop_notify(FuriEventLoop* instance, FuriEventLoopFlag flag) {
|
||||||
|
if(FURI_IS_IRQ_MODE()) {
|
||||||
|
BaseType_t yield = pdFALSE;
|
||||||
|
|
||||||
|
(void)xTaskNotifyIndexedFromISR(
|
||||||
|
(TaskHandle_t)instance->thread_id,
|
||||||
|
FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX,
|
||||||
|
flag,
|
||||||
|
eSetBits,
|
||||||
|
&yield);
|
||||||
|
|
||||||
|
portYIELD_FROM_ISR(yield);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
(void)xTaskNotifyIndexed(
|
||||||
|
(TaskHandle_t)instance->thread_id, FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX, flag, eSetBits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void furi_event_loop_stop(FuriEventLoop* instance) {
|
void furi_event_loop_stop(FuriEventLoop* instance) {
|
||||||
furi_check(instance);
|
furi_check(instance);
|
||||||
|
furi_event_loop_notify(instance, FuriEventLoopFlagStop);
|
||||||
xTaskNotifyIndexed(
|
|
||||||
(TaskHandle_t)instance->thread_id,
|
|
||||||
FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX,
|
|
||||||
FuriEventLoopFlagStop,
|
|
||||||
eSetBits);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -268,11 +300,7 @@ void furi_event_loop_pend_callback(
|
|||||||
|
|
||||||
PendingQueue_push_front(instance->pending_queue, item);
|
PendingQueue_push_front(instance->pending_queue, item);
|
||||||
|
|
||||||
xTaskNotifyIndexed(
|
furi_event_loop_notify(instance, FuriEventLoopFlagPending);
|
||||||
(TaskHandle_t)instance->thread_id,
|
|
||||||
FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX,
|
|
||||||
FuriEventLoopFlagPending,
|
|
||||||
eSetBits);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -328,6 +356,17 @@ static void furi_event_loop_object_subscribe(
|
|||||||
* Public specialized subscription API
|
* Public specialized subscription API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void furi_event_loop_subscribe_event_flag(
|
||||||
|
FuriEventLoop* instance,
|
||||||
|
FuriEventFlag* event_flag,
|
||||||
|
FuriEventLoopEvent event,
|
||||||
|
FuriEventLoopEventCallback callback,
|
||||||
|
void* context) {
|
||||||
|
extern const FuriEventLoopContract furi_event_flag_event_loop_contract;
|
||||||
|
furi_event_loop_object_subscribe(
|
||||||
|
instance, event_flag, &furi_event_flag_event_loop_contract, event, callback, context);
|
||||||
|
}
|
||||||
|
|
||||||
void furi_event_loop_subscribe_message_queue(
|
void furi_event_loop_subscribe_message_queue(
|
||||||
FuriEventLoop* instance,
|
FuriEventLoop* instance,
|
||||||
FuriMessageQueue* message_queue,
|
FuriMessageQueue* message_queue,
|
||||||
@@ -491,11 +530,7 @@ static void furi_event_loop_item_notify(FuriEventLoopItem* instance) {
|
|||||||
|
|
||||||
FURI_CRITICAL_EXIT();
|
FURI_CRITICAL_EXIT();
|
||||||
|
|
||||||
xTaskNotifyIndexed(
|
furi_event_loop_notify(owner, FuriEventLoopFlagEvent);
|
||||||
(TaskHandle_t)owner->thread_id,
|
|
||||||
FURI_EVENT_LOOP_FLAG_NOTIFY_INDEX,
|
|
||||||
FuriEventLoopFlagEvent,
|
|
||||||
eSetBits);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool furi_event_loop_item_is_waiting(FuriEventLoopItem* instance) {
|
static bool furi_event_loop_item_is_waiting(FuriEventLoopItem* instance) {
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
* provide any compatibility with other event driven APIs. But
|
* provide any compatibility with other event driven APIs. But
|
||||||
* programming concepts are the same, except some runtime
|
* programming concepts are the same, except some runtime
|
||||||
* limitations from our side.
|
* limitations from our side.
|
||||||
|
*
|
||||||
|
* @warning Only ONE instance of FuriEventLoop per thread is possible. ALL FuriEventLoop
|
||||||
|
* funcitons MUST be called from the same thread that the instance was created in.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -197,10 +200,29 @@ typedef void FuriEventLoopObject;
|
|||||||
*
|
*
|
||||||
* @param object The object that triggered the event
|
* @param object The object that triggered the event
|
||||||
* @param context The context that was provided upon subscription
|
* @param context The context that was provided upon subscription
|
||||||
*
|
|
||||||
* @return true if event was processed, false if we need to delay processing
|
|
||||||
*/
|
*/
|
||||||
typedef bool (*FuriEventLoopEventCallback)(FuriEventLoopObject* object, void* context);
|
typedef void (*FuriEventLoopEventCallback)(FuriEventLoopObject* object, void* context);
|
||||||
|
|
||||||
|
/** Opaque event flag type */
|
||||||
|
typedef struct FuriEventFlag FuriEventFlag;
|
||||||
|
|
||||||
|
/** Subscribe to event flag events
|
||||||
|
*
|
||||||
|
* @warning you can only have one subscription for one event type.
|
||||||
|
*
|
||||||
|
* @param instance The Event Loop instance
|
||||||
|
* @param event_flag The event flag to add
|
||||||
|
* @param[in] event The Event Loop event to trigger on
|
||||||
|
* @param[in] callback The callback to call on event
|
||||||
|
* @param context The context for callback
|
||||||
|
*/
|
||||||
|
|
||||||
|
void furi_event_loop_subscribe_event_flag(
|
||||||
|
FuriEventLoop* instance,
|
||||||
|
FuriEventFlag* event_flag,
|
||||||
|
FuriEventLoopEvent event,
|
||||||
|
FuriEventLoopEventCallback callback,
|
||||||
|
void* context);
|
||||||
|
|
||||||
/** Opaque message queue type */
|
/** Opaque message queue type */
|
||||||
typedef struct FuriMessageQueue FuriMessageQueue;
|
typedef struct FuriMessageQueue FuriMessageQueue;
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ typedef enum {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
FuriEventLoopProcessStatusComplete,
|
FuriEventLoopProcessStatusComplete,
|
||||||
FuriEventLoopProcessStatusIncomplete,
|
FuriEventLoopProcessStatusIncomplete,
|
||||||
FuriEventLoopProcessStatusAgain,
|
|
||||||
FuriEventLoopProcessStatusFreeLater,
|
FuriEventLoopProcessStatusFreeLater,
|
||||||
} FuriEventLoopProcessStatus;
|
} FuriEventLoopProcessStatus;
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ void furi_event_loop_link_notify(FuriEventLoopLink* instance, FuriEventLoopEvent
|
|||||||
|
|
||||||
typedef FuriEventLoopLink* (*FuriEventLoopContractGetLink)(FuriEventLoopObject* object);
|
typedef FuriEventLoopLink* (*FuriEventLoopContractGetLink)(FuriEventLoopObject* object);
|
||||||
|
|
||||||
typedef uint32_t (
|
typedef bool (
|
||||||
*FuriEventLoopContractGetLevel)(FuriEventLoopObject* object, FuriEventLoopEvent event);
|
*FuriEventLoopContractGetLevel)(FuriEventLoopObject* object, FuriEventLoopEvent event);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* @file event_loop_timer.h
|
* @file event_loop_timer.h
|
||||||
* @brief Software timer functionality for FuriEventLoop.
|
* @brief Software timer functionality for FuriEventLoop.
|
||||||
|
*
|
||||||
|
* @warning ALL FuriEventLoopTimer functions MUST be called from the
|
||||||
|
* same thread that the owner FuriEventLoop instance was created in.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ static FuriEventLoopLink* furi_message_queue_event_loop_get_link(FuriEventLoopOb
|
|||||||
return &instance->event_loop_link;
|
return &instance->event_loop_link;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static bool
|
||||||
furi_message_queue_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {
|
furi_message_queue_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {
|
||||||
FuriMessageQueue* instance = object;
|
FuriMessageQueue* instance = object;
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
|
|||||||
@@ -144,13 +144,13 @@ static FuriEventLoopLink* furi_mutex_event_loop_get_link(FuriEventLoopObject* ob
|
|||||||
return &instance->event_loop_link;
|
return &instance->event_loop_link;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static bool
|
||||||
furi_mutex_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {
|
furi_mutex_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {
|
||||||
FuriMutex* instance = object;
|
FuriMutex* instance = object;
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
|
|
||||||
if(event == FuriEventLoopEventIn || event == FuriEventLoopEventOut) {
|
if(event == FuriEventLoopEventIn || event == FuriEventLoopEventOut) {
|
||||||
return furi_mutex_get_owner(instance) ? 0 : 1;
|
return !furi_mutex_get_owner(instance);
|
||||||
} else {
|
} else {
|
||||||
furi_crash();
|
furi_crash();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ static FuriEventLoopLink* furi_semaphore_event_loop_get_link(FuriEventLoopObject
|
|||||||
return &instance->event_loop_link;
|
return &instance->event_loop_link;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static bool
|
||||||
furi_semaphore_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {
|
furi_semaphore_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {
|
||||||
FuriSemaphore* instance = object;
|
FuriSemaphore* instance = object;
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ static FuriEventLoopLink* furi_stream_buffer_event_loop_get_link(FuriEventLoopOb
|
|||||||
return &stream_buffer->event_loop_link;
|
return &stream_buffer->event_loop_link;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static bool
|
||||||
furi_stream_buffer_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {
|
furi_stream_buffer_event_loop_get_level(FuriEventLoopObject* object, FuriEventLoopEvent event) {
|
||||||
FuriStreamBuffer* stream_buffer = object;
|
FuriStreamBuffer* stream_buffer = object;
|
||||||
furi_assert(stream_buffer);
|
furi_assert(stream_buffer);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,77.3,,
|
Version,+,78.0,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
@@ -1120,6 +1120,7 @@ Function,+,furi_event_loop_is_subscribed,_Bool,"FuriEventLoop*, FuriEventLoopObj
|
|||||||
Function,+,furi_event_loop_pend_callback,void,"FuriEventLoop*, FuriEventLoopPendingCallback, void*"
|
Function,+,furi_event_loop_pend_callback,void,"FuriEventLoop*, FuriEventLoopPendingCallback, void*"
|
||||||
Function,+,furi_event_loop_run,void,FuriEventLoop*
|
Function,+,furi_event_loop_run,void,FuriEventLoop*
|
||||||
Function,+,furi_event_loop_stop,void,FuriEventLoop*
|
Function,+,furi_event_loop_stop,void,FuriEventLoop*
|
||||||
|
Function,+,furi_event_loop_subscribe_event_flag,void,"FuriEventLoop*, FuriEventFlag*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
||||||
Function,+,furi_event_loop_subscribe_message_queue,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
Function,+,furi_event_loop_subscribe_message_queue,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
||||||
Function,+,furi_event_loop_subscribe_mutex,void,"FuriEventLoop*, FuriMutex*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
Function,+,furi_event_loop_subscribe_mutex,void,"FuriEventLoop*, FuriMutex*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
||||||
Function,+,furi_event_loop_subscribe_semaphore,void,"FuriEventLoop*, FuriSemaphore*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
Function,+,furi_event_loop_subscribe_semaphore,void,"FuriEventLoop*, FuriSemaphore*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
||||||
|
|||||||
|
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,77.3,,
|
Version,+,78.0,,
|
||||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||||
@@ -1230,6 +1230,7 @@ Function,+,furi_event_loop_is_subscribed,_Bool,"FuriEventLoop*, FuriEventLoopObj
|
|||||||
Function,+,furi_event_loop_pend_callback,void,"FuriEventLoop*, FuriEventLoopPendingCallback, void*"
|
Function,+,furi_event_loop_pend_callback,void,"FuriEventLoop*, FuriEventLoopPendingCallback, void*"
|
||||||
Function,+,furi_event_loop_run,void,FuriEventLoop*
|
Function,+,furi_event_loop_run,void,FuriEventLoop*
|
||||||
Function,+,furi_event_loop_stop,void,FuriEventLoop*
|
Function,+,furi_event_loop_stop,void,FuriEventLoop*
|
||||||
|
Function,+,furi_event_loop_subscribe_event_flag,void,"FuriEventLoop*, FuriEventFlag*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
||||||
Function,+,furi_event_loop_subscribe_message_queue,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
Function,+,furi_event_loop_subscribe_message_queue,void,"FuriEventLoop*, FuriMessageQueue*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
||||||
Function,+,furi_event_loop_subscribe_mutex,void,"FuriEventLoop*, FuriMutex*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
Function,+,furi_event_loop_subscribe_mutex,void,"FuriEventLoop*, FuriMutex*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
||||||
Function,+,furi_event_loop_subscribe_semaphore,void,"FuriEventLoop*, FuriSemaphore*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
Function,+,furi_event_loop_subscribe_semaphore,void,"FuriEventLoop*, FuriSemaphore*, FuriEventLoopEvent, FuriEventLoopEventCallback, void*"
|
||||||
|
|||||||
|
Reference in New Issue
Block a user