mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-23 13:21:44 +03:00
6d823835df
* 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>
139 lines
5.5 KiB
C
139 lines
5.5 KiB
C
/**
|
|
* @file example_event_loop_mutex.c
|
|
* @brief Example application that demonstrates the FuriEventLoop and FuriMutex integration.
|
|
*
|
|
* This application simulates a use case where a time-consuming blocking operation is executed
|
|
* in a separate thread and a mutex is being used for synchronization. The application runs 10 iterations
|
|
* of the above mentioned simulated work and prints the results to the debug output each time, then exits.
|
|
*/
|
|
|
|
#include <furi.h>
|
|
#include <furi_hal_random.h>
|
|
|
|
#define TAG "ExampleEventLoopMutex"
|
|
|
|
#define WORKER_ITERATION_COUNT (10)
|
|
// We are interested in IN events (for the mutex, that means that the mutex has been released),
|
|
// using edge trigger mode (reacting only to changes in mutex state) and
|
|
// employing one-shot mode to automatically unsubscribe before the event is processed.
|
|
#define MUTEX_EVENT_AND_FLAGS \
|
|
(FuriEventLoopEventIn | FuriEventLoopEventFlagEdge | FuriEventLoopEventFlagOnce)
|
|
|
|
typedef struct {
|
|
FuriEventLoop* event_loop;
|
|
FuriThread* worker_thread;
|
|
FuriMutex* worker_mutex;
|
|
uint8_t worker_result;
|
|
} EventLoopMutexApp;
|
|
|
|
// This funciton is being run in a separate thread to simulate lenghty blocking operations
|
|
static int32_t event_loop_mutex_app_worker_thread(void* context) {
|
|
furi_assert(context);
|
|
EventLoopMutexApp* app = context;
|
|
|
|
FURI_LOG_I(TAG, "Worker thread started");
|
|
|
|
// Run 10 iterations of simulated work
|
|
for(uint32_t i = 0; i < WORKER_ITERATION_COUNT; ++i) {
|
|
FURI_LOG_I(TAG, "Doing work ...");
|
|
// Take the mutex so that no-one can access the worker_result variable
|
|
furi_check(furi_mutex_acquire(app->worker_mutex, FuriWaitForever) == FuriStatusOk);
|
|
// Simulate a blocking operation with a random delay between 900 and 1100 ms
|
|
const uint32_t work_time_ms = 900 + furi_hal_random_get() % 200;
|
|
furi_delay_ms(work_time_ms);
|
|
// Simulate a result with a random number between 0 and 255
|
|
app->worker_result = furi_hal_random_get() % 0xFF;
|
|
|
|
FURI_LOG_I(TAG, "Work done in %lu ms", work_time_ms);
|
|
// Release the mutex, which will notify the event loop that the result is ready
|
|
furi_check(furi_mutex_release(app->worker_mutex) == FuriStatusOk);
|
|
// Return control to the scheduler so that the event loop can take the mutex in its turn
|
|
furi_thread_yield();
|
|
}
|
|
|
|
FURI_LOG_I(TAG, "All work done, worker thread out!");
|
|
// Request the event loop to stop
|
|
furi_event_loop_stop(app->event_loop);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// This function is being run each time when the mutex gets released
|
|
static void event_loop_mutex_app_event_callback(FuriEventLoopObject* object, void* context) {
|
|
furi_assert(context);
|
|
|
|
EventLoopMutexApp* app = context;
|
|
furi_assert(object == app->worker_mutex);
|
|
|
|
// Take the mutex so that no-one can access the worker_result variable
|
|
// IMPORTANT: the wait time MUST be 0, i.e. the event loop event callbacks
|
|
// must NOT ever block. If it is possible that the mutex will be taken by
|
|
// others, then the event callback code must take it into account.
|
|
furi_check(furi_mutex_acquire(app->worker_mutex, 0) == FuriStatusOk);
|
|
// Access the worker_result variable and print it.
|
|
FURI_LOG_I(TAG, "Result available! Value: %u", app->worker_result);
|
|
// Release the mutex, enabling the worker thread to continue when it's ready
|
|
furi_check(furi_mutex_release(app->worker_mutex) == FuriStatusOk);
|
|
// Subscribe for the mutex release events again, since we were unsubscribed automatically
|
|
// before processing the event.
|
|
furi_event_loop_subscribe_mutex(
|
|
app->event_loop,
|
|
app->worker_mutex,
|
|
MUTEX_EVENT_AND_FLAGS,
|
|
event_loop_mutex_app_event_callback,
|
|
app);
|
|
}
|
|
|
|
static EventLoopMutexApp* event_loop_mutex_app_alloc(void) {
|
|
EventLoopMutexApp* app = malloc(sizeof(EventLoopMutexApp));
|
|
|
|
// Create an event loop instance.
|
|
app->event_loop = furi_event_loop_alloc();
|
|
// Create a worker thread instance.
|
|
app->worker_thread = furi_thread_alloc_ex(
|
|
"EventLoopMutexWorker", 1024, event_loop_mutex_app_worker_thread, app);
|
|
// Create a mutex instance.
|
|
app->worker_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
|
// Subscribe for the mutex release events.
|
|
// Note that since FuriEventLoopEventFlagOneShot is used, we will be automatically unsubscribed
|
|
// from events before entering the event processing callback. This is necessary in order to not
|
|
// trigger on events caused by releasing the mutex in the callback.
|
|
furi_event_loop_subscribe_mutex(
|
|
app->event_loop,
|
|
app->worker_mutex,
|
|
MUTEX_EVENT_AND_FLAGS,
|
|
event_loop_mutex_app_event_callback,
|
|
app);
|
|
|
|
return app;
|
|
}
|
|
|
|
static void event_loop_mutex_app_free(EventLoopMutexApp* app) {
|
|
// 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->worker_mutex);
|
|
// Delete all instances
|
|
furi_thread_free(app->worker_thread);
|
|
furi_mutex_free(app->worker_mutex);
|
|
furi_event_loop_free(app->event_loop);
|
|
|
|
free(app);
|
|
}
|
|
|
|
static void event_loop_mutex_app_run(EventLoopMutexApp* app) {
|
|
furi_thread_start(app->worker_thread);
|
|
furi_event_loop_run(app->event_loop);
|
|
furi_thread_join(app->worker_thread);
|
|
}
|
|
|
|
// The application's entry point - referenced in application.fam
|
|
int32_t example_event_loop_mutex_app(void* arg) {
|
|
UNUSED(arg);
|
|
|
|
EventLoopMutexApp* app = event_loop_mutex_app_alloc();
|
|
event_loop_mutex_app_run(app);
|
|
event_loop_mutex_app_free(app);
|
|
|
|
return 0;
|
|
}
|